├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── Package.swift ├── README.md ├── Sources └── WiremockClient │ ├── Networking │ ├── Endpoint.swift │ ├── NetworkService.swift │ ├── RequestMethod.swift │ ├── ResponseObjects │ │ ├── LoggedRequest.swift │ │ ├── LoggedRequestResponse.swift │ │ └── MappingsResponse.swift │ ├── WiremockClientNetworkService.swift │ └── WiremockEndpoint.swift │ ├── Stubbing │ ├── MatchCondition.swift │ ├── RequestMapping.swift │ ├── RequestPattern.swift │ ├── ResponseDefinition.swift │ ├── StubMapping.swift │ ├── Transformer.swift │ └── UrlPattern.swift │ ├── Utils │ └── DebugOverridable.swift │ ├── WiremockClient.swift │ └── WiremockClientError.swift ├── Tests ├── LinuxMain.swift └── WiremockClientTests │ ├── IntegrationTests.swift │ ├── JSON │ └── test.json │ ├── ResponseDefinitionTests.swift │ ├── StubMappingTests.swift │ ├── WiremockClientErrorTests.swift │ ├── WiremockClientNetworkServiceTests.swift │ └── WiremockClientTests.swift ├── WiremockClient.podspec └── WiremockClient.xcodeproj ├── WiremockClientTests_Info.plist ├── WiremockClient_Info.plist ├── project.pbxproj ├── project.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcshareddata └── xcschemes ├── WiremockClient.xcscheme ├── WiremockClientTests.xcscheme └── xcschememanagement.plist /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | Carthage 26 | # We recommend against adding the Pods directory to your .gitignore. However 27 | # you should judge for yourself, the pros and cons are mentioned at: 28 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 29 | # 30 | # Note: if you ignore the Pods directory, make sure to uncomment 31 | # `pod install` in .travis.yml 32 | # 33 | # Pods/ 34 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode7.3 6 | language: objective-c 7 | # cache: cocoapods 8 | # podfile: Example/Podfile 9 | # before_install: 10 | # - gem install cocoapods # Since Travis is not always on latest version 11 | # - pod install --project-directory=Example 12 | script: 13 | - set -o pipefail && xcodebuild test -workspace Example/WiremockClient.xcworkspace -scheme WiremockClient-Example -sdk iphonesimulator9.3 ONLY_ACTIVE_ARCH=NO | xcpretty 14 | - pod lib lint 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | ## 3.0.0 - 2023-02-14 8 | ### Added 9 | - Support for Swift Package Manager. 10 | - StubMapping `withRequestBodyFromLocalJsonFile` method to support defining mappings via local JSON files. 11 | ### Changed 12 | - Bumped iOS deployment target to 14.0. 13 | - Bumped macOS deployment target to 11.0. 14 | 15 | ## 2.1.0 - 2022-05-26 16 | ### Changed 17 | - Adjusted access levels to allow stub and mapping properties to be read externally. 18 | 19 | ## 2.0.0 - 2021-03-21 20 | ### Added 21 | - Support for adding a delay to individual responses. 22 | - Support for deleting request history. 23 | - Unit test coverage. 24 | ### Removed 25 | - Public `json` and `data` properties from ResponseDefinition. 26 | ### Changed 27 | - Public WiremockClient methods now throw errors. 28 | - ResponseDefinition `withLocalJsonBodyFile` method updated to accept a Bundle reference instead of a `String`. 29 | 30 | ## 1.4.0 - 2020-09-04 31 | ### Added 32 | - Support for adding a global delay to all Wiremock server responses 33 | - Documentation to all public classes and methods 34 | 35 | ## 1.3.0 - 2020-02-11 36 | ### Added 37 | - CHANGELOG.md 38 | - Supported Swift versions in podspec (4.0, 5.0) 39 | - Support for transformers in ResponseDefinition 40 | - Minimum deployment target for tvOS to podspec 41 | - Support for verifying requests made to the Wiremock server 42 | ### Removed 43 | - .swift-version file 44 | 45 | 46 | ## 1.2.1 - 2018-06-15 47 | ### Changed 48 | - Made all URLSession data tasks synchronous 49 | 50 | ## 1.2.0 - 2018-02-26 51 | ### Added 52 | - Minimum deployment target for macOS to podspec 53 | - Support for shutting down server Wiremock instance 54 | - Support for checking if Wiremock instance is running 55 | - Access to ResponseDefinition data 56 | - Support for SPM 57 | 58 | ## 1.1.2 - 2017-10-19 59 | ### Changed 60 | - .swift-version file syntax 61 | - README 62 | ### Added 63 | - Support for handling JSON array objects in ResponseDefinition 64 | 65 | ## 1.1.1 - 2017-10-12 66 | ### Added 67 | - Support for adding headers to ResponseDefinition 68 | 69 | ## 1.1.0 - 2017-09-11 70 | ### Added 71 | - Access to ResponseDefinition JSON 72 | 73 | ## 1.0.0 - 2017-07-26 74 | ### Added 75 | - WiremockClient 1.0.0 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 theodore.rothrock@gmail.com 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "WiremockClient", 8 | products: [ 9 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 10 | .library( 11 | name: "WiremockClient", 12 | targets: ["WiremockClient"]), 13 | ], 14 | dependencies: [ 15 | // Dependencies declare other packages that this package depends on. 16 | // .package(url: /* package url */, from: "1.0.0"), 17 | ], 18 | targets: [ 19 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 20 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 21 | .target( 22 | name: "WiremockClient", 23 | dependencies: []), 24 | .testTarget( 25 | name: "WiremockClientTests", 26 | dependencies: ["WiremockClient"]), 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # WiremockClient 2 | 3 | [![Version](https://img.shields.io/cocoapods/v/WiremockClient.svg?style=flat)](http://cocoapods.org/pods/WiremockClient) 4 | [![License](https://img.shields.io/cocoapods/l/WiremockClient.svg?style=flat)](http://cocoapods.org/pods/WiremockClient) 5 | [![Platform](https://img.shields.io/cocoapods/p/WiremockClient.svg?style=flat)](http://cocoapods.org/pods/WiremockClient) 6 | 7 | WiremockClient is an HTTP client that allows users to interact with a standalone Wiremock instance from within an Xcode project. 8 | 9 | ## Installation 10 | 11 | WiremockClient is available through [CocoaPods](http://cocoapods.org) and [Swift Package Manager](https://swift.org/package-manager/). 12 | 13 | To install with CocoaPods, simply add the following line to your Podfile: 14 | 15 | ```ruby 16 | pod "WiremockClient" 17 | ``` 18 | 19 | To install with SwiftPM, add the following line to the `dependencies` section of your Package.swift: 20 | 21 | ```swift 22 | .package(url: "https://github.com/mobileforming/WiremockClient", .upToNextMajor(from: Version(major: 2, minor: 2, patch: 0))) 23 | ``` 24 | 25 | ## Usage 26 | 27 | WiremockClient maps closely to the functionality available in Wiremock's native Java API as described in the [documentation](http://wiremock.org/docs/). The pod enables you to build and post JSON mappings to a standalone Wiremock instance from within an Xcode project. It is assumed that you are familiar with the basics of using Wiremock, including initializing a standalone instance and populating it with mappings. 28 | 29 | ### Getting Started 30 | 31 | To begin using WiremockClient, start up a standalone Wiremock instance on localhost port 8080. The base URL for all requests is set to `http://localhost:8080` by default and can be modified at the top of the `WiremockClient` file. Be sure to [whitelist your base URL](https://developer.apple.com/library/content/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW33) before using WiremockClient, or you will be unable to communicate with your standalone instance. 32 | 33 | ### Posting a Mapping 34 | 35 | The following code will post a mapping to your Wiremock standalone instance that will match any request sent to the `http://localhost:8080/my/path` endpoint and return a status of 200: 36 | 37 | ```swift 38 | WiremockClient.postMapping(stubMapping: 39 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 40 | .willReturn( 41 | ResponseDefinition() 42 | .withStatus(200) 43 | ) 44 | ) 45 | ``` 46 | 47 | This behavior maps closely to the Java API as described in the [Stubbing](http://wiremock.org/docs/stubbing/) portion of the documentation, so a full explanation will not be reproduced here. There are three significant differences to note: 48 | 1. The `stubFor()` method used to initialize a mapping in the Java API is now a type function of the `StubMapping` class. 49 | 2. The `aResponse()` method used to initialize a response object in the Java API has been replaced with an instance of the `ResponseDefinition` class. 50 | 3. The mapping created using the `stubFor` and `aResponse` methods above must be passed to the `WiremockClient.postMapping(stubMapping: StubMapping)` function to be posted to the standalone Wiremock instance. 51 | 52 | ### Matching a Request 53 | 54 | All of the request matching logic available in the Java API has been reproduced within WiremockClient. For a full explanation of the methods below, reference the [Request Matching](http://wiremock.org/docs/request-matching/) portion of the documentation. 55 | 56 | A collection of `StubMapping` instance methods enable the user to build mappings step-by-step, specifying the criteria that must be met in order for an incoming network request to be considered a match: 57 | 58 | ```swift 59 | WiremockClient.postMapping(stubMapping: 60 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 61 | .withHeader("Accept", matchCondition: .contains, value: "xml") 62 | .withCookie("session", matchCondition: .matches, value: ".*12345.*") 63 | .withQueryParam("search_term", matchCondition: .equalTo, value: "WireMock") 64 | .withBasicAuth(username: "myUsername", password: "myPassword") 65 | .withRequestBody(.equalTo, value: "Some request body string") 66 | .willReturn( 67 | ResponseDefinition() 68 | ) 69 | ) 70 | ``` 71 | 72 | An additional `withRequestBodyEqualToJson` method has been added to allow users to set the `ignoreArrayOrder` and `ignoreExtraElements` flags described in the ‘JSON equality’ section of the [Request Matching](http://wiremock.org/docs/request-matching/) documentation: 73 | 74 | ```swift 75 | WiremockClient.postMapping(stubMapping: 76 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 77 | .withRequestBodyEqualToJson(jsonString: "{ \"total_results\": 4 }", ignoreArrayOrder: true, ignoreExtraElements: true) 78 | .willReturn(ResponseDefinition()) 79 | ) 80 | ``` 81 | 82 | WiremockClient also includes a convenience method for stubbing request JSON that is stored in a local file: 83 | 84 | ```swift 85 | WiremockClient.postMapping(stubMapping: 86 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 87 | .withRequestBodyFromLocalJsonFile(fileName: "myFile", in: Bundle(for: type(of: self))) 88 | .willReturn(ResponseDefinition()) 89 | ) 90 | ``` 91 | 92 | Mappings can also be prioritized as described in the ‘Stub priority’ section of the [Stubbing](http://wiremock.org/docs/stubbing/) documentation: 93 | 94 | ```swift 95 | WiremockClient.postMapping(stubMapping: 96 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 97 | .withPriority(1) 98 | .willReturn(ResponseDefinition()) 99 | ) 100 | ``` 101 | 102 | ### Defining a Response 103 | 104 | All of the response definition logic available in the Java API has been reproduced in WiremockClient. For a full explanation of the methods below, reference the [Stubbing](http://wiremock.org/docs/stubbing/) portion of the documentation. 105 | 106 | A collection of `ResponseDefinition` instance methods enable the user to specify elements to include in the response that is returned when a mapping is matched: 107 | 108 | ```swift 109 | WiremockClient.postMapping(stubMapping: 110 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 111 | .willReturn( 112 | ResponseDefinition() 113 | .withStatus(200) 114 | .withStatusMessage("Great jorb!") 115 | .withHeaders(["Pragma": "no-cache", "Connection": "keep-alive"]) 116 | .withBody("Just a plain old text body") 117 | ) 118 | ) 119 | ``` 120 | 121 | WiremockClient also includes a convenience method for returning JSON that is stored in a local file: 122 | 123 | ```swift 124 | WiremockClient.postMapping(stubMapping: 125 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 126 | .willReturn( 127 | ResponseDefinition() 128 | .withLocalJsonBodyFile(fileName: "myFile", in: Bundle(for: type(of: self))) 129 | ) 130 | ) 131 | ``` 132 | 133 | ### Proxying 134 | 135 | As in the Java API, requests can be proxied through to other hosts. A full explanation of this method can be found in the [Proxying](http://wiremock.org/docs/proxying/) portion of the documentation: 136 | 137 | ```swift 138 | WiremockClient.postMapping(stubMapping: 139 | StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 140 | .willReturn( 141 | ResponseDefinition() 142 | .proxiedFrom("http://myproxyhost.gov") 143 | ) 144 | ) 145 | ``` 146 | 147 | ### Stateful Behavior 148 | 149 | WiremockClient also supports scenarios as described in the [Stateful Behavior](http://wiremock.org/docs/stateful-behaviour/) portion of the documentation: 150 | 151 | ```swift 152 | WiremockClient.postMapping(stubMapping: 153 | StubMapping.stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 154 | .inScenario("Scenario Title") 155 | .whenScenarioStateIs("Required Scenario State") 156 | .willSetStateTo("New Scenario State") 157 | .willReturn( 158 | ResponseDefinition() 159 | .withStatus(200) 160 | ) 161 | ) 162 | ``` 163 | 164 | The following method resets all scenarios to their default state (“Started”): 165 | 166 | ```swift 167 | WiremockClient.resetAllScenarios() 168 | ``` 169 | 170 | ### Updating a Mapping 171 | 172 | Updating a mapping requires a reference to it’s UUID. When a mapping is created, a UUID is automatically assigned to it. However, it is also possible to assign a UUID manually and cache it in a variable for future reference. In the example below, a mapping is posted that returns a status code of 200 when matched. The mapping is then updated to return a status code of 404: 173 | 174 | ```swift 175 | let myMappingID = UUID() 176 | 177 | WiremockClient.postMapping(stubMapping: 178 | StubMapping.stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 179 | .withUUID(myMappingID) 180 | .willReturn( 181 | ResponseDefinition() 182 | .withStatus(200) 183 | ) 184 | ) 185 | 186 | WiremockClient.updateMapping(uuid: myMappingID, stubMapping: 187 | StubMapping.stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 188 | .willReturn( 189 | ResponseDefinition() 190 | .withStatus(404) 191 | ) 192 | ) 193 | ``` 194 | 195 | ### Deleting Mappings 196 | 197 | Similar to updating a mapping, deleting a mapping requires a reference to it’s UUID: 198 | 199 | ```swift 200 | let myMappingID = UUID() 201 | 202 | WiremockClient.postMapping(stubMapping: 203 | StubMapping.stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/my/path") 204 | .withUUID(myMappingID) 205 | .willReturn( 206 | ResponseDefinition() 207 | .withStatus(200) 208 | ) 209 | ) 210 | 211 | WiremockClient.deleteMapping(uuid: myMappingID) 212 | ``` 213 | 214 | It is also possible to reset your Wiremock instance by deleting all mappings simultaneously: 215 | 216 | ```swift 217 | WiremockClient.reset() 218 | ``` 219 | 220 | ### Saving Mappings 221 | 222 | Mappings can be persisted to the `mappings` directory of your Wiremock instance via the following method: 223 | 224 | ```swift 225 | WiremockClient.saveAllMappings() 226 | ``` 227 | 228 | ### Using WiremockClient in an XCTestCase 229 | 230 | A typical use case of WiremockClient looks like this: 231 | 1. Call `WiremockClient.postMapping()` in the test suite’s `setup()` method to post the required mappings before the app launches. 232 | 2. If necessary, call `WiremockClient.updateMapping()` within the test script to alter mappings on the fly. 233 | 3. Call `WiremockClient.reset()` in the test suite’s `tearDown()` method to remove all mappings after the test has finished. 234 | 235 | ## Author 236 | 237 | Ted Rothrock, theodore.rothrock@gmail.com 238 | 239 | ## License 240 | 241 | WiremockClient is available under the MIT license. See the LICENSE file for more info. 242 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/Endpoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Endpoint.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Endpoint { 11 | var path: String { get } 12 | var requestMethod: RequestMethod { get } 13 | var body: Data? { get } 14 | var urlRequest: URLRequest? { get } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/NetworkService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkService.swift 3 | // WiremockClientPackageDescription 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NetworkService { 11 | func makeSynchronousRequest(with endpoint: Endpoint) throws 12 | func makeSynchronousRequest(with endpoint: Endpoint) throws -> T 13 | } 14 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/RequestMethod.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestMethod.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/23/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// A collection of HTTP request methods 12 | public enum RequestMethod: String, Codable { 13 | case GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD, TRACE, ANY 14 | } 15 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/ResponseObjects/LoggedRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggedRequest.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | The example json from wiremock.org when only requests are returned. 12 | This is associated with the verify method: 13 | http://wiremock.org/docs/verifying 14 | 15 | "url": "/my/other/url", 16 | "absoluteUrl": "http://my.other.domain.com/my/other/url", 17 | "method": "POST", 18 | "headers": { 19 | "Accept": "text/plain", 20 | "Content-Type": "text/plain" 21 | }, 22 | "body": "My text", 23 | "browserProxyRequest": false, 24 | "loggedDate": 1339083581823, 25 | "loggedDateString": "2012-06-07 16:39:41" 26 | 27 | */ 28 | 29 | /// An object representing an entry in the Wiremock server's request journal. 30 | public struct LoggedRequest: Codable { 31 | public var url: String? 32 | public var absoluteUrl: String? 33 | public var method: RequestMethod? 34 | public var body: String? 35 | public var browserProxyRequest: Bool? 36 | public var loggedDateString: String? 37 | public var headers: [String : String]? 38 | } 39 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/ResponseObjects/LoggedRequestResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoggedRequestResponse.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/23/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct LoggedRequestResponse: Codable { 11 | var requests: [LoggedRequest] 12 | } 13 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/ResponseObjects/MappingsResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MappingsResponse.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MappingsResponse: Decodable { 11 | 12 | var isServerRunning: Bool { 13 | return metaData != nil 14 | } 15 | 16 | init(total: Int) { 17 | metaData = MetaData(total: total) 18 | } 19 | 20 | private var metaData: MetaData? 21 | 22 | private enum CodingKeys : String, CodingKey { 23 | case metaData = "meta" 24 | } 25 | 26 | private struct MetaData: Decodable { 27 | var total: Int 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/WiremockClientNetworkService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClientNetworkService.swift 3 | // WiremockClientPackageDescription 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol SynchronousURLSession { 11 | func executeSynchronousRequest(_ request: URLRequest) -> Result 12 | } 13 | 14 | extension URLSession: SynchronousURLSession { 15 | func executeSynchronousRequest(_ request: URLRequest) -> Result { 16 | var data: Data? 17 | var error: Error? 18 | let semaphore = DispatchSemaphore(value: 0) 19 | let task = dataTask(with: request) { responseData, _, responseError in 20 | data = responseData 21 | error = responseError 22 | semaphore.signal() 23 | } 24 | task.resume() 25 | semaphore.wait() 26 | if let error = error { 27 | return .failure(error) 28 | } else { 29 | return .success(data) 30 | } 31 | } 32 | 33 | } 34 | 35 | struct WiremockClientNetworkService: NetworkService { 36 | 37 | @DebugOverridable 38 | var urlSession: SynchronousURLSession = URLSession.shared 39 | 40 | func makeSynchronousRequest(with endpoint: Endpoint) throws { 41 | guard let urlRequest = endpoint.urlRequest else { 42 | throw WiremockClientError.invalidUrl 43 | } 44 | 45 | let result = urlSession.executeSynchronousRequest(urlRequest) 46 | 47 | switch result { 48 | case .success: 49 | return 50 | case .failure(let error): 51 | throw error 52 | } 53 | } 54 | 55 | func makeSynchronousRequest(with endpoint: Endpoint) throws -> T { 56 | guard let urlRequest = endpoint.urlRequest else { 57 | throw WiremockClientError.invalidUrl 58 | } 59 | 60 | let result = urlSession.executeSynchronousRequest(urlRequest) 61 | 62 | switch result { 63 | case .success(let data): 64 | guard let data = data else { 65 | throw WiremockClientError.decodingError 66 | } 67 | return try JSONDecoder().decode(T.self, from: data) 68 | case .failure(let error): 69 | throw error 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Networking/WiremockEndpoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockEndpoint.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum WiremockEndpoint: Endpoint { 11 | case deleteMapping(uuid: UUID) 12 | case deleteRequestLog 13 | case getRequestLog(mapping: RequestMapping) 14 | case postMapping(mapping: StubMapping) 15 | case resetScenarios 16 | case resetServer 17 | case saveAllMappings 18 | case setGlobalDelay(delay: UInt) 19 | case shutDownServer 20 | case updateMapping(uuid: UUID, mapping: StubMapping) 21 | case verifyServerIsRunning 22 | 23 | var path: String { 24 | switch self { 25 | case .deleteMapping(let uuid): 26 | return "__admin/mappings/\(uuid.uuidString)" 27 | case .deleteRequestLog: 28 | return "__admin/requests" 29 | case .getRequestLog: 30 | return "__admin/requests/find" 31 | case .postMapping: 32 | return "__admin/mappings" 33 | case .resetScenarios: 34 | return "__admin/scenarios/reset" 35 | case .resetServer: 36 | return "__admin/reset" 37 | case .saveAllMappings: 38 | return "__admin/mappings/save" 39 | case .setGlobalDelay: 40 | return "__admin/settings" 41 | case .shutDownServer: 42 | return "__admin/shutdown" 43 | case .updateMapping(let uuid, _): 44 | return "__admin/mappings/\(uuid.uuidString)" 45 | case .verifyServerIsRunning: 46 | return "__admin/mappings" 47 | } 48 | } 49 | 50 | var requestMethod: RequestMethod { 51 | switch self { 52 | case .deleteMapping, 53 | .deleteRequestLog: 54 | return .DELETE 55 | case .getRequestLog, 56 | .postMapping, 57 | .resetScenarios, 58 | .resetServer, 59 | .saveAllMappings, 60 | .setGlobalDelay, 61 | .shutDownServer: 62 | return .POST 63 | case .updateMapping: 64 | return .PUT 65 | case .verifyServerIsRunning: 66 | return .GET 67 | } 68 | } 69 | 70 | var body: Data? { 71 | switch self { 72 | case .getRequestLog(let mapping): 73 | return mapping.asData() 74 | case .postMapping(let mapping): 75 | return mapping.asData() 76 | case .deleteMapping, 77 | .deleteRequestLog, 78 | .resetScenarios, 79 | .resetServer, 80 | .saveAllMappings, 81 | .shutDownServer, 82 | .verifyServerIsRunning: 83 | return nil 84 | case .setGlobalDelay(let delay): 85 | return try? JSONSerialization.data(withJSONObject: ["fixedDelay": delay], options: [.prettyPrinted]) 86 | case .updateMapping(_, let mapping): 87 | return mapping.asData() 88 | } 89 | } 90 | 91 | var urlRequest: URLRequest? { 92 | guard let url = URL(string: "\(WiremockClient.baseURL)/\(path)") else { 93 | return nil 94 | } 95 | var urlRequest = URLRequest(url: url) 96 | urlRequest.httpMethod = requestMethod.rawValue 97 | urlRequest.httpBody = body 98 | return urlRequest 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/MatchCondition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MatchCondition.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A collection of conditons used to determine matches based on request attributes including headers, cookies, parameters, request body, JSON, XML, and XPath 11 | public enum MatchCondition: String, CaseIterable { 12 | /// An exact match 13 | case equalTo 14 | 15 | /// A match containing a value 16 | case contains 17 | 18 | /// A match by regex pattern 19 | case matches 20 | 21 | /// A negative match 22 | case doesNotMatch 23 | 24 | /// A semantic match of valid JSON 25 | case equalToJson 26 | 27 | /// A JSON attribute non-nil value match 28 | case matchesJsonPath 29 | 30 | /// A semantic match of valid XML 31 | case equalToXml 32 | 33 | /// An XML path non-nil value match 34 | case matchesXPath 35 | } 36 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/RequestMapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestMapping.swift 3 | // WiremockClientPackageDescription 4 | // 5 | // Created by Tony Eichelberger on 10/30/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /// An object used to configure a Wiremock request verification mapping. Refer to http://http://wiremock.org/docs/verifying/ for more details. 11 | public class RequestMapping { 12 | 13 | public private(set) var request: RequestPattern 14 | 15 | private init(requestMethod: RequestMethod, urlMatchCondition: URLMatchCondition, url: String) { 16 | self.request = RequestPattern(requestMethod: requestMethod, urlMatchCondition: urlMatchCondition, url: url) 17 | } 18 | 19 | //---------------------------------- 20 | // MARK: Mapping Builder Methods 21 | //---------------------------------- 22 | 23 | /// Initializes a `RequestMapping` 24 | /// 25 | /// - Parameter requestMethod: The HTTP request method to match 26 | /// - Parameter urlMatchCondition: A condition used to match the URL path and parameters of a request to a mapping 27 | /// - Parameter url: The URL path and parameter pattern that the mapping will use to match requests 28 | /// - Returns: A configured `RequestMapping` 29 | public static func requestFor(requestMethod: RequestMethod, urlMatchCondition: URLMatchCondition, url: String) -> RequestMapping { 30 | return RequestMapping(requestMethod: requestMethod, urlMatchCondition: urlMatchCondition, url: url) 31 | } 32 | 33 | /// Adds a header match condition to a mapping 34 | /// 35 | /// - Parameter key: The header key 36 | /// - Parameter matchCondition: The condition under which the header will be evaluated as a match 37 | /// - Parameter value: The header value 38 | /// - Returns: The `RequestMapping` with an updated header match condition 39 | public func withHeader(_ key: String, matchCondition: MatchCondition, value: String) -> RequestMapping { 40 | self.request.headers = self.request.headers ?? [String: [String: String]]() 41 | let headerDict = [matchCondition.rawValue: value] 42 | self.request.headers?[key] = headerDict 43 | return self 44 | } 45 | 46 | /// Adds a cookie match condition to a mapping 47 | /// 48 | /// - Parameter key: The cookie key 49 | /// - Parameter matchCondition: The condition under which the cookie will be evaluated as a match 50 | /// - Parameter value: The cookie value 51 | /// - Returns: The `RequestMapping` with an updated cookie match condition 52 | public func withCookie(_ key: String, matchCondition: MatchCondition, value: String) -> RequestMapping { 53 | self.request.cookies = self.request.cookies ?? [String: [String: String]]() 54 | let cookieDict = [matchCondition.rawValue: value] 55 | self.request.cookies?[key] = cookieDict 56 | return self 57 | } 58 | 59 | /// Adds a query parameter match condition to a mapping 60 | /// 61 | /// - Parameter param: The param key 62 | /// - Parameter matchCondition: The condition under which the param will be evaluated as a match 63 | /// - Parameter value: The param value 64 | /// - Returns: The `RequestMapping` with an updated param match condition 65 | public func withQueryParam(_ param: String, matchCondition: MatchCondition, value: String) -> RequestMapping { 66 | self.request.queryParameters = self.request.queryParameters ?? [String: [String:Any]]() 67 | let paramDict = [matchCondition.rawValue: value] 68 | self.request.queryParameters?[param] = paramDict 69 | return self 70 | } 71 | 72 | /// Adds a basic authentication match condition to a mapping 73 | /// 74 | /// - Parameter username: The username to evaluate 75 | /// - Parameter password: The password to evaluate 76 | /// - Returns: The `RequestMapping` with an updated basic authentication match condition 77 | public func withBasicAuth(username: String, password: String) -> RequestMapping { 78 | self.request.basicAuthCredentials = [Constants.keyUsername: username, Constants.keyPassword: password] 79 | return self 80 | } 81 | 82 | /// Adds a request body match condition to a mapping 83 | /// 84 | /// - Parameter matchCondition: The condition under which the body will be evaluated as a match 85 | /// - Parameter value: The body value 86 | /// - Returns: The `RequestMapping` with an updated body match condition 87 | public func withRequestBody(_ matchCondition: MatchCondition, value: String) -> RequestMapping { 88 | self.request.bodyPatterns = self.request.bodyPatterns ?? [[String: Any]]() 89 | let bodyPatternDict = [matchCondition.rawValue: value] 90 | self.request.bodyPatterns?.append(bodyPatternDict) 91 | return self 92 | } 93 | 94 | /// Adds a request JSON match condition to a mapping 95 | /// 96 | /// - Parameter jsonString: The request JSON value in `String` form 97 | /// - Parameter ignoreArrayOrder: A flag that indicates if matching JSON must contain elements in an exact order 98 | /// - Parameter ignoreExtraElements: A flag that indicates if matching JSON must not contain extra elements 99 | /// - Returns: The `RequestMapping` with an updated JSON match condition 100 | public func withRequestBodyEqualToJson(jsonString: String, ignoreArrayOrder: Bool, ignoreExtraElements: Bool) -> RequestMapping { 101 | self.request.bodyPatterns = self.request.bodyPatterns ?? [[String: Any]]() 102 | var bodyPatternDict: [String: Any] = [MatchCondition.equalToJson.rawValue: jsonString] 103 | if ignoreArrayOrder { 104 | bodyPatternDict[Constants.keyIgnoreArrayOrder] = true 105 | } 106 | if ignoreExtraElements { 107 | bodyPatternDict[Constants.keyIgnoreExtraElements] = true 108 | } 109 | self.request.bodyPatterns?.append(bodyPatternDict) 110 | return self 111 | } 112 | 113 | //---------------------------------- 114 | // MARK: Mapping to Data Conversion 115 | //---------------------------------- 116 | 117 | // Mapping Key Names 118 | 119 | enum Constants { 120 | static let keyMethod = "method" 121 | static let keyParams = "queryParameters" 122 | static let keyHeaders = "headers" 123 | static let keyCookies = "cookies" 124 | static let keyBasicAuth = "basicAuthCredentials" 125 | static let keyBodyPatterns = "bodyPatterns" 126 | static let keyIgnoreArrayOrder = "ignoreArrayOrder" 127 | static let keyIgnoreExtraElements = "ignoreExtraElements" 128 | static let keyUsername = "username" 129 | static let keyPassword = "password" 130 | } 131 | 132 | func asDict() -> [String: Any] { 133 | var requestDict = [String: Any]() 134 | 135 | // URL 136 | requestDict["\(self.request.urlPattern.urlMatchCondition.rawValue)"] = "\(self.request.urlPattern.url)" 137 | 138 | // Request Method 139 | requestDict[Constants.keyMethod] = "\(self.request.requestMethod.rawValue)" 140 | 141 | // Headers 142 | if let headers = self.request.headers { 143 | requestDict[Constants.keyHeaders] = headers 144 | } 145 | 146 | // Cookies 147 | if let cookies = self.request.cookies { 148 | requestDict[Constants.keyCookies] = cookies 149 | } 150 | 151 | // Query Parameters 152 | if let queryParameters = self.request.queryParameters { 153 | requestDict[Constants.keyParams] = queryParameters 154 | } 155 | 156 | // Basic Auth Credentials 157 | if let credentials = self.request.basicAuthCredentials { 158 | requestDict[Constants.keyBasicAuth] = credentials 159 | } 160 | 161 | // Request Body Patterns 162 | if let bodyPatterns = self.request.bodyPatterns { 163 | requestDict[Constants.keyBodyPatterns] = bodyPatterns 164 | } 165 | 166 | return requestDict 167 | } 168 | 169 | func asData() -> Data? { 170 | return try? JSONSerialization.data(withJSONObject: asDict(), options: [.prettyPrinted]) 171 | } 172 | 173 | } 174 | 175 | extension RequestMapping: CustomStringConvertible { 176 | /// A `String` representation of the mapping 177 | public var description: String { 178 | guard let data = asData(), let stringVal = String(data: data, encoding: .utf8) else { 179 | return "{}" 180 | } 181 | return stringVal 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/RequestPattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestPattern.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/23/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Conditions used to match the URL path and parameters of a request to a mapping 12 | public enum URLMatchCondition: String { 13 | 14 | /// Exact match of both path and query parameters 15 | case urlEqualTo = "url" 16 | 17 | /// Match by path and query parameters with option to include regex 18 | case urlMatching = "urlPattern" 19 | 20 | /// Exact match by path only 21 | case urlPathEqualTo = "urlPath" 22 | 23 | /// Match by path with option to include regex 24 | case urlPathMatching = "urlPathPattern" 25 | } 26 | 27 | public struct RequestPattern { 28 | var urlPattern: UrlPattern 29 | var requestMethod: RequestMethod 30 | var queryParameters: [String: [String: Any]]? 31 | var headers: [String: [String: String]]? 32 | var cookies: [String: [String: String]]? 33 | var basicAuthCredentials: [String: String]? 34 | var bodyPatterns: [[String: Any]]? 35 | 36 | init(requestMethod: RequestMethod, urlMatchCondition: URLMatchCondition, url: String) { 37 | self.requestMethod = requestMethod 38 | self.urlPattern = UrlPattern(url: url, urlMatchCondition: urlMatchCondition) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/ResponseDefinition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDefinition.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/23/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private enum ResponseDefintionError: Error { 12 | case missingBundle 13 | case fileNotFound 14 | case unableToConvertData 15 | } 16 | 17 | extension ResponseDefintionError: LocalizedError { 18 | public var errorDescription: String? { 19 | switch self { 20 | case .missingBundle: 21 | return "Unable to locate resource bundle." 22 | case .fileNotFound: 23 | return "Unable to locate file in resource bundle." 24 | case .unableToConvertData: 25 | return "Unable to convert data to string." 26 | } 27 | } 28 | } 29 | 30 | /// An object used to define the response to be returned by a Wiremock server. Refer to http://http://wiremock.org/docs/stubbing/ for more details. 31 | public class ResponseDefinition { 32 | 33 | var status: Int? 34 | var fixedDelay: Int? 35 | var statusMessage: String? 36 | var body: String? 37 | var proxyBaseUrl: String? 38 | var bodyFileName: String? 39 | var headers: [String: String]? 40 | var transformers: [Transformer]? 41 | 42 | public init() {} 43 | 44 | //---------------------------------- 45 | // MARK: Response Builder Methods 46 | //---------------------------------- 47 | 48 | /// Adds a header to the Wiremock server response 49 | /// 50 | /// - Parameter key: The header key 51 | /// - Parameter value: The header value 52 | /// - Returns: The `ResponseDefinition` with updated headers 53 | public func withHeader(key: String, value: String) -> ResponseDefinition { 54 | self.headers = self.headers ?? [String: String]() 55 | self.headers?[key] = value 56 | return self 57 | } 58 | 59 | /// Adds multiple headers to the Wiremock server response 60 | /// 61 | /// - Parameter headers: The headers to add 62 | /// - Returns: The `ResponseDefinition` with updated headers 63 | public func withHeaders(_ headers: [String: String] = [:]) -> ResponseDefinition { 64 | self.headers = headers 65 | return self 66 | } 67 | 68 | /// Updates the HTTP status code of the Wiremock server response 69 | /// 70 | /// - Parameter status: An HTTP status code 71 | /// - Returns: The `ResponseDefinition` with an updated status code 72 | public func withStatus(_ status: Int) -> ResponseDefinition { 73 | self.status = status 74 | return self 75 | } 76 | 77 | /// Updates the `statusMessage` of the Wiremock server response 78 | /// 79 | /// - Parameter statusMessage: A status message 80 | /// - Returns: The `ResponseDefinition` with an updated status message 81 | public func withStatusMessage(_ statusMessage: String) -> ResponseDefinition { 82 | self.statusMessage = statusMessage 83 | return self 84 | } 85 | 86 | /// Updates the body of the Wiremock server response 87 | /// 88 | /// - Parameter body: The body to return. Supported objects include `String`, `[String: Any]`, and `[[String: Any]]`. 89 | /// - Returns: The `ResponseDefinition` with an updated body 90 | public func withBody(_ body: Any) -> ResponseDefinition { 91 | switch body { 92 | 93 | case let string as String: 94 | self.body = string 95 | break 96 | 97 | case let json as [String: Any]: 98 | do { 99 | let data = try JSONSerialization.data(withJSONObject: json, options: []) 100 | guard let jsonString = String(data: data, encoding: .utf8) else { 101 | throw ResponseDefintionError.unableToConvertData 102 | } 103 | self.body = jsonString 104 | } catch { 105 | print("Error adding body to ResponseDefinition: \(error.localizedDescription)") 106 | } 107 | break 108 | 109 | case let json as [[String: Any]]: 110 | do { 111 | let data = try JSONSerialization.data(withJSONObject: json, options: []) 112 | guard let jsonString = String(data: data, encoding: .utf8) else { 113 | throw ResponseDefintionError.unableToConvertData 114 | } 115 | self.body = jsonString 116 | } catch { 117 | print("Error adding body to ResponseDefinition: \(error.localizedDescription)") 118 | } 119 | break 120 | 121 | default: 122 | print("Unable to handle response body of type \(type(of: body))") 123 | break 124 | } 125 | return self 126 | } 127 | 128 | /// Updates the Wiremock server file name with which to populate the response body 129 | /// 130 | /// - Parameter fileName: The name of a file located in the __files directory of the Wiremock server. Its contents will be used to populate the response body. 131 | /// - Returns: The `ResponseDefinition` with an updated body file name 132 | public func withBodyFile(_ fileName: String) -> ResponseDefinition { 133 | self.bodyFileName = fileName 134 | return self 135 | } 136 | 137 | /// Updates the body of the Wiremock server response using the contents of a local JSON file. 138 | /// - Parameters: 139 | /// - fileName: The name of a local JSON file. Its contents will be used to populate the reponse body. 140 | /// - bundle: The `Bundle` in which the JSON file is located. 141 | /// - subdirectory: The path to the file. 142 | /// - Returns: The `ResponseDefinition` with an updated local JSON body file 143 | public func withLocalJsonBodyFile(_ fileName: String, in bundle: Bundle, subdirectory: String? = nil) -> ResponseDefinition { 144 | do { 145 | guard let responseUrl = bundle.url(forResource: fileName, withExtension: "json", subdirectory: subdirectory) else { 146 | throw ResponseDefintionError.fileNotFound 147 | } 148 | let data = try Data(contentsOf: responseUrl) 149 | let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) 150 | let dataWithoutSpecialChars = try JSONSerialization.data(withJSONObject: json, options: []) 151 | guard let jsonString = String(data: dataWithoutSpecialChars, encoding: .utf8) else { 152 | throw ResponseDefintionError.unableToConvertData 153 | } 154 | self.body = jsonString 155 | } catch { 156 | print("Error adding body to ResponseDefinition from file \(fileName): \(error.localizedDescription)") 157 | } 158 | return self 159 | } 160 | 161 | /// Updates the proxy URL from which the response is returned. Matching requests will be proxied to this URL. 162 | /// 163 | /// - Parameter urlString: The proxy URL string 164 | /// - Returns: The `ResponseDefinition` with an updated proxy URL 165 | public func proxiedFrom(_ urlString: String) -> ResponseDefinition { 166 | self.proxyBaseUrl = urlString 167 | return self 168 | } 169 | 170 | /// Updates the array of `Transformer` options included in the response 171 | /// 172 | /// - Parameter transformers: The `Transformer` options to include 173 | /// - Returns: The `ResponseDefinition` with updated `Transformer` options 174 | public func withTransformers(_ transformers: [Transformer]) -> ResponseDefinition { 175 | self.transformers = transformers 176 | return self 177 | } 178 | 179 | /// Adds a delay to a specific response 180 | /// 181 | /// - Parameter fixedDelay: The time interval in milliseconds by which to delay the response 182 | /// - Returns: The `ResponseDefinition` with added delay to it 183 | public func withFixedDelay(_ fixedDelay: Int) -> ResponseDefinition { 184 | self.fixedDelay = fixedDelay 185 | return self 186 | } 187 | 188 | //---------------------------------- 189 | // MARK: Mapping to Data Conversion 190 | //---------------------------------- 191 | 192 | enum Constants { 193 | static let keyBody = "body" 194 | static let keyBodyFile = "bodyFileName" 195 | static let keyHeaders = "headers" 196 | static let keyProxyUrl = "proxyBaseUrl" 197 | static let keyStatus = "status" 198 | static let keyStatusMessage = "statusMessage" 199 | static let keyTransformers = "transformers" 200 | static let keyFixedDelay = "fixedDelayMilliseconds" 201 | } 202 | 203 | func asDict() -> [String: Any] { 204 | var responseDict = [String: Any]() 205 | 206 | // Body 207 | if let responseBody = body { 208 | responseDict[Constants.keyBody] = responseBody 209 | } 210 | 211 | // Status 212 | if let responseStatus = status { 213 | responseDict[Constants.keyStatus] = responseStatus 214 | } 215 | 216 | // Status Message 217 | if let statusMessage = statusMessage { 218 | responseDict[Constants.keyStatusMessage] = statusMessage 219 | } 220 | 221 | // Body File Name 222 | if let bodyFileName = bodyFileName { 223 | responseDict[Constants.keyBodyFile] = bodyFileName 224 | } 225 | 226 | // Proxy Base URL 227 | if let proxyBaseUrl = proxyBaseUrl { 228 | responseDict[Constants.keyProxyUrl] = proxyBaseUrl 229 | } 230 | 231 | // Headers 232 | if let headers = headers { 233 | responseDict[Constants.keyHeaders] = headers 234 | } 235 | 236 | // Transformers 237 | if let transformers = transformers { 238 | responseDict[Constants.keyTransformers] = transformers.map { $0.rawValue } 239 | } 240 | 241 | // Fixed Delay 242 | if let fixedDelay = fixedDelay { 243 | responseDict[Constants.keyFixedDelay] = fixedDelay 244 | } 245 | 246 | return responseDict 247 | } 248 | } 249 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/StubMapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubMapping.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/23/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | private enum StubMappingError: Error { 12 | case missingBundle 13 | case fileNotFound 14 | case unableToConvertData 15 | } 16 | 17 | extension StubMappingError: LocalizedError { 18 | public var errorDescription: String? { 19 | switch self { 20 | case .missingBundle: 21 | return "Unable to locate resource bundle." 22 | case .fileNotFound: 23 | return "Unable to locate file in resource bundle." 24 | case .unableToConvertData: 25 | return "Unable to convert data to string." 26 | } 27 | } 28 | } 29 | 30 | /// An object used to configure a Wiremock server stub mapping. Refer to http://wiremock.org/docs/stubbing/ and http://wiremock.org/docs/request-matching/ for more details. 31 | public class StubMapping { 32 | public private(set) var request: RequestMapping 33 | public private(set) var response: ResponseDefinition? 34 | public private(set) var uuid: UUID 35 | public private(set) var name: String? 36 | public private(set) var priority: Int? 37 | public private(set) var scenarioName: String? 38 | public private(set) var requiredScenarioState: String? 39 | public private(set) var newScenarioState: String? 40 | 41 | private init(requestMethod: RequestMethod, urlMatchCondition: URLMatchCondition, url: String) { 42 | self.request = RequestMapping.requestFor(requestMethod: requestMethod, urlMatchCondition: urlMatchCondition, url: url) 43 | self.uuid = UUID() 44 | } 45 | 46 | //---------------------------------- 47 | // MARK: Mapping Builder Methods 48 | //---------------------------------- 49 | 50 | /// Initializes a `StubMapping` 51 | /// 52 | /// - Parameter requestMethod: The HTTP request method to match 53 | /// - Parameter urlMatchCondition: A condition used to match the URL path and parameters of a request to a mapping 54 | /// - Parameter url: The URL path and parameter pattern that the mapping will use to match requests 55 | /// - Returns: A configured `StubMapping` 56 | public static func stubFor(requestMethod: RequestMethod, urlMatchCondition: URLMatchCondition, url: String) -> StubMapping { 57 | return StubMapping(requestMethod: requestMethod, urlMatchCondition: urlMatchCondition, url: url) 58 | } 59 | 60 | /// Adds a unique identifier to a mapping 61 | /// 62 | /// - Parameter uuid: The unique identifier to add 63 | /// - Returns: The `StubMapping` with an updated identifier 64 | public func withUUID(_ uuid: UUID) -> StubMapping { 65 | self.uuid = uuid 66 | return self 67 | } 68 | 69 | /// Adds a priority ranking to a mapping. Used to determine the order in which multiple matching mappings are evaluated. 70 | /// 71 | /// - Parameter priority: An `Int` representing the mapping's priority. 1 is the highest priority. 72 | /// - Returns: The `StubMapping` with an updated priority 73 | public func withPriority(_ priority: Int) -> StubMapping { 74 | self.priority = priority 75 | return self 76 | } 77 | 78 | /// Adds a scenario name to a mapping. This is an identifier used to group scenario states. 79 | /// 80 | /// - Parameter scenarioName: The scenario name to add 81 | /// - Returns: The `StubMapping` with an updated scenario name 82 | public func inScenario(_ scenarioName: String) -> StubMapping { 83 | self.scenarioName = scenarioName 84 | return self 85 | } 86 | 87 | /// Adds a required scenario state to a mapping. The mapping will only match a request when the matching scenario state is active. 88 | /// - Parameter scenarioState: The name of the required scenario state 89 | /// - Returns: The `StubMapping` with an updated required scenario state 90 | public func whenScenarioStateIs(_ scenarioState: String) -> StubMapping { 91 | self.requiredScenarioState = scenarioState 92 | return self 93 | } 94 | 95 | /// Adds a new scenario state to a mapping. Matching this mapping will update the scenario state to this new value. 96 | /// 97 | /// - Parameter scenarioState: The name of the new scenario state 98 | /// - Returns: The `StubMapping` with an updated new scenario state 99 | public func willSetStateTo(_ scenarioState: String) -> StubMapping { 100 | self.newScenarioState = scenarioState 101 | return self 102 | } 103 | 104 | /// Adds a header match condition to a mapping 105 | /// 106 | /// - Parameter key: The header key 107 | /// - Parameter matchCondition: The condition under which the header will be evaluated as a match 108 | /// - Parameter value: The header value 109 | /// - Returns: The `StubMapping` with an updated header match condition 110 | public func withHeader(_ key: String, matchCondition: MatchCondition, value: String) -> StubMapping { 111 | _ = self.request.withHeader(key, matchCondition: matchCondition, value: value) 112 | return self 113 | } 114 | 115 | /// Adds a cookie match condition to a mapping 116 | /// 117 | /// - Parameter key: The cookie key 118 | /// - Parameter matchCondition: The condition under which the cookie will be evaluated as a match 119 | /// - Parameter value: The cookie value 120 | /// - Returns: The `StubMapping` with an updated cookie match condition 121 | public func withCookie(_ key: String, matchCondition: MatchCondition, value: String) -> StubMapping { 122 | _ = self.request.withCookie(key, matchCondition: matchCondition, value: value) 123 | return self 124 | } 125 | 126 | /// Adds a query parameter match condition to a mapping 127 | /// 128 | /// - Parameter param: The param key 129 | /// - Parameter matchCondition: The condition under which the param will be evaluated as a match 130 | /// - Parameter value: The param value 131 | /// - Returns: The `StubMapping` with an updated param match condition 132 | public func withQueryParam(_ param: String, matchCondition: MatchCondition, value: String) -> StubMapping { 133 | _ = self.request.withQueryParam(param, matchCondition: matchCondition, value: value) 134 | return self 135 | } 136 | 137 | /// Adds a basic authentication match condition to a mapping 138 | /// 139 | /// - Parameter username: The username to evaluate 140 | /// - Parameter password: The password to evaluate 141 | /// - Returns: The `StubMapping` with an updated basic authentication match condition 142 | public func withBasicAuth(username: String, password: String) -> StubMapping { 143 | _ = self.request.withBasicAuth(username: username, password: password) 144 | return self 145 | } 146 | 147 | /// Adds a request body match condition to a mapping 148 | /// 149 | /// - Parameter matchCondition: The condition under which the body will be evaluated as a match 150 | /// - Parameter value: The body value 151 | /// - Returns: The `StubMapping` with an updated body match condition 152 | public func withRequestBody(_ matchCondition: MatchCondition, value: String) -> StubMapping { 153 | _ = self.request.withRequestBody(matchCondition, value: value) 154 | return self 155 | } 156 | 157 | /// Adds a request JSON match condition to a mapping 158 | /// 159 | /// - Parameter jsonString: The request JSON value in `String` form 160 | /// - Parameter ignoreArrayOrder: A flag that indicates if matching JSON must contain elements in an exact order 161 | /// - Parameter ignoreExtraElements: A flag that indicates if matching JSON must not contain extra elements 162 | /// - Returns: The `StubMapping` with an updated JSON match condition 163 | public func withRequestBodyEqualToJson(jsonString: String, ignoreArrayOrder: Bool, ignoreExtraElements: Bool) -> StubMapping { 164 | _ = self.request.withRequestBodyEqualToJson(jsonString: jsonString, ignoreArrayOrder: ignoreArrayOrder, ignoreExtraElements: ignoreExtraElements) 165 | return self 166 | } 167 | 168 | /// Adds a request JSON match condition to a mapping using the contents of a local JSON file. 169 | /// - Parameters: 170 | /// - fileName: The name of a local JSON file. Its contents will be used to populate the reponse body. 171 | /// - bundle: The `Bundle` in which the JSON file is located. 172 | /// - ignoreArrayOrder: A flag that indicates if matching JSON must contain elements in an exact order 173 | /// - ignoreExtraElements: A flag that indicates if matching JSON must not contain extra elements 174 | /// - subdirectory: The path to the file. 175 | /// - Returns: The `StubMapping` with an updated JSON match condition 176 | public func withRequestBodyFromLocalJsonFile(_ fileName: String, in bundle: Bundle, ignoreArayOrder: Bool, ignoreExtraElements: Bool, subdirectory: String? = nil) -> StubMapping { 177 | do { 178 | guard let responseUrl = bundle.url(forResource: fileName, withExtension: "json", subdirectory: subdirectory) else { 179 | throw StubMappingError.fileNotFound 180 | } 181 | let data = try Data(contentsOf: responseUrl) 182 | let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) 183 | let dataWithoutSpecialChars = try JSONSerialization.data(withJSONObject: json, options: []) 184 | guard let jsonString = String(data: dataWithoutSpecialChars, encoding: .utf8) else { 185 | throw StubMappingError.unableToConvertData 186 | } 187 | _ = self.withRequestBodyEqualToJson(jsonString: jsonString, ignoreArrayOrder: ignoreArayOrder, ignoreExtraElements: ignoreExtraElements) 188 | } catch { 189 | print("Error adding body to StubMapping from file \(fileName): \(error.localizedDescription)") 190 | } 191 | return self 192 | } 193 | 194 | /// Adds a response object to a mapping 195 | /// 196 | /// - Parameter response: The `ResponseDefinition` object used to configure the Wiremock server response 197 | /// - Returns: The `StubMapping` with an updated `ResponseDefinition` 198 | public func willReturn(_ response: ResponseDefinition) -> StubMapping { 199 | self.response = response 200 | return self 201 | } 202 | 203 | //---------------------------------- 204 | // MARK: Mapping to Data Conversion 205 | //---------------------------------- 206 | 207 | // Mapping Key Names 208 | 209 | enum Constants { 210 | static let keyUuid = "uuid" 211 | static let keyPriority = "priority" 212 | static let keyScenName = "scenarioName" 213 | static let keyReqScen = "requiredScenarioState" 214 | static let keyNewScen = "newScenarioState" 215 | static let keyRequest = "request" 216 | static let keyResponse = "response" 217 | } 218 | 219 | func asDict() -> [String: Any] { 220 | var mappingDict = [String: Any]() 221 | 222 | // UUID 223 | mappingDict[Constants.keyUuid] = self.uuid.uuidString 224 | 225 | // Priority 226 | if let priority = self.priority { 227 | mappingDict[Constants.keyPriority] = priority 228 | } 229 | 230 | // Scenarios 231 | if let scenarioName = self.scenarioName { 232 | mappingDict[Constants.keyScenName] = scenarioName 233 | } 234 | 235 | if let requiredScenarioState = self.requiredScenarioState { 236 | mappingDict[Constants.keyReqScen] = requiredScenarioState 237 | } 238 | 239 | if let newScenarioState = self.newScenarioState { 240 | mappingDict[Constants.keyNewScen] = newScenarioState 241 | } 242 | 243 | // Request 244 | mappingDict[Constants.keyRequest] = self.request.asDict() 245 | 246 | // Response 247 | if let response = self.response { 248 | mappingDict[Constants.keyResponse] = response.asDict() 249 | } 250 | 251 | return mappingDict 252 | } 253 | 254 | func asData() -> Data? { 255 | return try? JSONSerialization.data(withJSONObject: asDict(), options: [.prettyPrinted]) 256 | } 257 | } 258 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/Transformer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Transformer.swift 3 | // WiremockClient 4 | // 5 | // Created by Kyle Ohanian on 7/18/19. 6 | // 7 | 8 | import Foundation 9 | 10 | public enum Transformer: String { 11 | case responseTemplate = "response-template" 12 | } 13 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Stubbing/UrlPattern.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UrlPattern.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/23/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct UrlPattern { 12 | 13 | var url: String 14 | var urlMatchCondition: URLMatchCondition 15 | 16 | init(url: String, urlMatchCondition: URLMatchCondition) { 17 | self.url = url 18 | self.urlMatchCondition = urlMatchCondition 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/WiremockClient/Utils/DebugOverridable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DebugOverridable.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | @propertyWrapper 11 | struct DebugOverridable { 12 | #if DEBUG 13 | var wrappedValue: Value 14 | #else 15 | let wrappedValue: Value 16 | #endif 17 | } 18 | -------------------------------------------------------------------------------- /Sources/WiremockClient/WiremockClient.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClient.swift 3 | // NewWiremockClient 4 | // 5 | // Created by Ted Rothrock on 6/24/17. 6 | // Copyright © 2017 Ted Rothrock. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public struct WiremockClient { 12 | 13 | /// The URL at which the Wiremock instance to be configured is running. 14 | public static var baseURL = "http://localhost:8080" 15 | 16 | /// A service responsible for executing network requests. Only overridable for test purposes. 17 | @DebugOverridable 18 | static var networkService: NetworkService = WiremockClientNetworkService() 19 | } 20 | 21 | // MARK: - Mappings 22 | 23 | extension WiremockClient { 24 | 25 | /// Adds a stub mapping to the Wiremock server. 26 | /// 27 | /// - Parameter stubMapping: The stub mapping to add 28 | public static func postMapping(stubMapping: StubMapping) throws { 29 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.postMapping(mapping: stubMapping)) 30 | } 31 | 32 | /// Replaces a stub mapping on the Wiremock server. 33 | /// 34 | /// - Parameter uuid: The identifier of the mapping to replace 35 | public static func updateMapping(uuid: UUID, stubMapping: StubMapping) throws { 36 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.updateMapping(uuid: uuid, mapping: stubMapping)) 37 | } 38 | 39 | /// Deletes a stub mapping from the Wiremock server. 40 | /// 41 | /// - Parameter uuid: The identifier of the mapping to delete 42 | public static func deleteMapping(uuid: UUID) throws { 43 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.deleteMapping(uuid: uuid)) 44 | } 45 | 46 | /// Persists all stub mappings to the `mappings` directory of the Wiremock server. 47 | public static func saveAllMappings() throws { 48 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.saveAllMappings) 49 | } 50 | 51 | } 52 | 53 | // MARK: - Requests 54 | 55 | extension WiremockClient { 56 | 57 | /// Looks up all requests matching a given pattern. 58 | /// 59 | /// - Parameter requestMapping: The request mapping to filter on 60 | /// - Returns: An array of LoggedRequest objects or an empty array if there was no match 61 | public static func findRequests(requestMapping: RequestMapping) throws -> [LoggedRequest] { 62 | let response: LoggedRequestResponse = try networkService.makeSynchronousRequest(with: WiremockEndpoint.getRequestLog(mapping: requestMapping)) 63 | return response.requests 64 | } 65 | 66 | /// Deletes all requests that have been recorded to this point 67 | public static func deleteAllRequests() throws { 68 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.deleteRequestLog) 69 | } 70 | 71 | /// Verifies that a request has been made to the Wiremock server at least once. 72 | /// 73 | /// - Parameter mapping: The request mapping to filter on 74 | /// - Throws: A verfication error if there was no matching request 75 | public static func verify(requestMapping: RequestMapping) throws { 76 | let requests = try findRequests(requestMapping: requestMapping) 77 | if requests.count < 1 { 78 | throw WiremockClientError.verificationError(description: "Did not find a matching request for the \(requestMapping) pattern") 79 | } 80 | } 81 | 82 | /// Verifies that a request has been made to the Wiremock server a specific number of times. 83 | /// 84 | /// - Parameter mapping: The request mapping to filter on 85 | /// - Throws: A verfication error if the request was not matched the expected number of times 86 | public static func verify(expectedCount: UInt, requestMapping: RequestMapping) throws { 87 | let requests = try findRequests(requestMapping: requestMapping) 88 | if requests.count != expectedCount { 89 | throw WiremockClientError.verificationError(description: "Did not find a matching request for the \(requestMapping) pattern") 90 | } 91 | } 92 | 93 | } 94 | 95 | // MARK: - Server State 96 | 97 | extension WiremockClient { 98 | 99 | /// Verifies that the server is running. 100 | /// 101 | /// - Returns: true if the server is running and ready to interact with 102 | public static func isServerRunning() throws -> Bool { 103 | let mappingsResponse: MappingsResponse = try networkService.makeSynchronousRequest(with: WiremockEndpoint.verifyServerIsRunning) 104 | return mappingsResponse.isServerRunning 105 | } 106 | 107 | /// Removes all stub mappings and deletes request logs from the Wiremock server. 108 | public static func reset() throws { 109 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.resetServer) 110 | } 111 | 112 | /// Resets the state of all scenarios to `start`. 113 | public static func resetAllScenarios() throws { 114 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.resetScenarios) 115 | } 116 | 117 | /// Shuts down the Wiremock server. 118 | public static func shutdownServer() throws { 119 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.shutDownServer) 120 | } 121 | 122 | /// Adds a delay to all responses from the Wiremock server. 123 | /// 124 | /// - Parameter delay: The time interval in milliseconds by which to delay all responses 125 | public static func setGlobalDelay(_ delay: UInt) throws { 126 | try networkService.makeSynchronousRequest(with: WiremockEndpoint.setGlobalDelay(delay: delay)) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Sources/WiremockClient/WiremockClientError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClientError.swift 3 | // WiremockClient 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum WiremockClientError: Error { 11 | case invalidUrl 12 | case verificationError(description: String) 13 | case decodingError 14 | case networkError 15 | } 16 | 17 | extension WiremockClientError: Equatable { 18 | static func == (lhs: WiremockClientError, rhs: WiremockClientError) -> Bool { 19 | switch (lhs, rhs) { 20 | case (.invalidUrl, .invalidUrl): 21 | return true 22 | case (.verificationError(let desc1), .verificationError(let desc2)): 23 | return desc1 == desc2 24 | case (.decodingError, .decodingError): 25 | return true 26 | case (.networkError, .networkError): 27 | return true 28 | default: 29 | return false 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import WiremockClientTests 3 | 4 | XCTMain([ 5 | testCase(WiremockClientTests.allTests), 6 | ]) 7 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/IntegrationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntegrationTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/23/21. 6 | // 7 | 8 | import XCTest 9 | import WiremockClient 10 | 11 | // Successful execution of this test suite requires a Wiremock server instance running on localhost:8080. 12 | 13 | class IntegrationTests: XCTestCase { 14 | 15 | override func setUpWithError() throws { 16 | guard try WiremockClient.isServerRunning() else { 17 | return XCTFail() 18 | } 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | try WiremockClient.reset() 23 | } 24 | 25 | func test_post_update_deleteMapping() throws { 26 | let urlString = "http://localhost:8080/test/path" 27 | let mappingID = UUID() 28 | 29 | // Post a mapping 30 | try WiremockClient 31 | .postMapping(stubMapping: StubMapping 32 | .stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "/test/path") 33 | .withUUID(mappingID) 34 | .willReturn(ResponseDefinition() 35 | .withBody(["key": "value"]) 36 | .withStatus(200))) 37 | 38 | let exp1 = XCTestExpectation(description: "Wait for network request") 39 | var json1: [String: String]? 40 | 41 | // Query the mapping 42 | executeNetworkRequest(with: urlString) { result in 43 | json1 = result 44 | exp1.fulfill() 45 | } 46 | wait(for: [exp1], timeout: 1.0) 47 | 48 | // Verify the result 49 | let unwrapped1 = try XCTUnwrap(json1) 50 | XCTAssertEqual(unwrapped1["key"], "value") 51 | 52 | // Update the mapping 53 | try WiremockClient.updateMapping(uuid: mappingID, stubMapping: StubMapping 54 | .stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "/test/path") 55 | .willReturn(ResponseDefinition() 56 | .withBody(["otherKey": "otherValue"]) 57 | .withStatus(200))) 58 | 59 | let exp2 = XCTestExpectation(description: "Wait for network request") 60 | var json2: [String: String]? 61 | 62 | // Query the mapping again 63 | executeNetworkRequest(with: urlString) { result in 64 | json2 = result 65 | exp2.fulfill() 66 | } 67 | wait(for: [exp2], timeout: 1.0) 68 | 69 | // Verify the result 70 | let unwrapped2 = try XCTUnwrap(json2) 71 | XCTAssertEqual(unwrapped2["otherKey"], "otherValue") 72 | 73 | // Delete the mapping 74 | try WiremockClient.deleteMapping(uuid: mappingID) 75 | 76 | let exp3 = XCTestExpectation(description: "Wait for network request") 77 | var json3: [String: String]? 78 | 79 | // Query the mapping again 80 | executeNetworkRequest(with: urlString) { result in 81 | json3 = result 82 | exp3.fulfill() 83 | } 84 | wait(for: [exp3], timeout: 1.0) 85 | 86 | // Verify that nothing is returned. 87 | XCTAssertNil(json3) 88 | } 89 | 90 | func test_requestQueries() throws { 91 | let urlString = "http://localhost:8080/test/path" 92 | 93 | // Make a request. 94 | let exp1 = XCTestExpectation(description: "Wait for network request") 95 | executeNetworkRequest(with: urlString) { _ in 96 | exp1.fulfill() 97 | } 98 | wait(for: [exp1], timeout: 1.0) 99 | 100 | // Verify that the request was registered. 101 | let requestMapping = RequestMapping.requestFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "/test/path") 102 | try WiremockClient.verify(requestMapping: requestMapping) 103 | 104 | // Make another request 105 | let exp2 = XCTestExpectation(description: "Wait for network request") 106 | executeNetworkRequest(with: urlString) { _ in 107 | exp2.fulfill() 108 | } 109 | wait(for: [exp2], timeout: 1.0) 110 | 111 | // Verify request count 112 | try WiremockClient.verify(expectedCount: 2, requestMapping: requestMapping) 113 | } 114 | 115 | // MARK: Networking 116 | 117 | private func executeNetworkRequest(with urlString: String, completion: @escaping ([String: String]?) -> Void) { 118 | guard let url = URL(string: urlString) else { 119 | completion(nil) 120 | return 121 | } 122 | let urlRequest = URLRequest(url: url) 123 | let task = URLSession.shared.dataTask(with: urlRequest) { (data, _, _) in 124 | guard let data = data else { 125 | completion(nil) 126 | return 127 | } 128 | let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: String] 129 | completion(json) 130 | } 131 | task.resume() 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/JSON/test.json: -------------------------------------------------------------------------------- 1 | { 2 | "testKey": "testValue" 3 | } 4 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/ResponseDefinitionTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ResponseDefinitionTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import XCTest 9 | @testable import WiremockClient 10 | 11 | class ResponseDefinitionTests: XCTestCase { 12 | 13 | func test_withHeader() throws { 14 | let key = "testKey" 15 | let value = "testValue" 16 | let response = ResponseDefinition().withHeader(key: key, value: value) 17 | let headers = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyHeaders] as? [String: Any]) 18 | XCTAssertEqual(headers[key] as? String, value) 19 | } 20 | 21 | func test_withHeaders() throws { 22 | let key1 = "testKey1" 23 | let value1 = "testValue1" 24 | let key2 = "testKey2" 25 | let value2 = "testValue2" 26 | let headers = [key1: value1, key2: value2] 27 | let response = ResponseDefinition().withHeaders(headers) 28 | let responseHeaders = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyHeaders] as? [String: Any]) 29 | XCTAssertEqual(responseHeaders[key1] as? String, value1) 30 | XCTAssertEqual(responseHeaders[key2] as? String, value2) 31 | } 32 | 33 | func test_withStatus() throws { 34 | let status = 200 35 | let response = ResponseDefinition().withStatus(status) 36 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyStatus] as? Int, status) 37 | } 38 | 39 | func test_withStatusMessage() throws { 40 | let statusMessage = "OK" 41 | let response = ResponseDefinition().withStatusMessage(statusMessage) 42 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyStatusMessage] as? String, statusMessage) 43 | } 44 | 45 | func test_withBody_string() throws { 46 | let bodyString = "Test body" 47 | let response = ResponseDefinition().withBody(bodyString) 48 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyBody] as? String, bodyString) 49 | } 50 | 51 | func test_withBody_dict_success() throws { 52 | let key1 = "testKey1" 53 | let value1 = "testValue1" 54 | let bodyDict = [key1: value1] 55 | let response = ResponseDefinition().withBody(bodyDict) 56 | let responseBody = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyBody] as? String) 57 | XCTAssertTrue(responseBody.contains("\"\(key1)\":\"\(value1)\"")) 58 | } 59 | 60 | func test_withBody_arrayOfDicts() throws { 61 | let key1 = "testKey1" 62 | let value1 = "testValue1" 63 | let key2 = "testKey2" 64 | let value2 = "testValue2" 65 | let dict1 = [key1: value1] 66 | let dict2 = [key2: value2] 67 | let response = ResponseDefinition().withBody([dict1, dict2]) 68 | let responseBody = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyBody] as? String) 69 | XCTAssertTrue(responseBody.contains("{\"\(key1)\":\"\(value1)\"}")) 70 | XCTAssertTrue(responseBody.contains("{\"\(key2)\":\"\(value2)\"}")) 71 | } 72 | 73 | func test_withBody_invalidType() throws { 74 | let response = ResponseDefinition().withBody(99) 75 | XCTAssertNil(response.asDict()[ResponseDefinition.Constants.keyBody]) 76 | } 77 | 78 | func test_withBodyFile() throws { 79 | let fileName = "testFile" 80 | let response = ResponseDefinition().withBodyFile(fileName) 81 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyBodyFile] as? String, fileName) 82 | } 83 | 84 | func test_withLocalJsonBodyFile_success() throws { 85 | let bundle = Bundle(for: type(of: self)) 86 | let response = ResponseDefinition().withLocalJsonBodyFile("test", in: bundle) 87 | let responseBody = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyBody] as? String) 88 | XCTAssertTrue(responseBody.contains("\"testKey\":\"testValue\"")) 89 | } 90 | 91 | func test_withLocalJsonBodyFile_failure() throws { 92 | let bundle = Bundle(for: type(of: self)) 93 | let response = ResponseDefinition().withLocalJsonBodyFile("noFile", in: bundle) 94 | XCTAssertNil(response.asDict()[ResponseDefinition.Constants.keyBody] as? String) 95 | } 96 | 97 | func test_proxiedFrom() throws { 98 | let proxyUrl = "http://localhost:8081" 99 | let response = ResponseDefinition().proxiedFrom(proxyUrl) 100 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyProxyUrl] as? String, proxyUrl) 101 | } 102 | 103 | func test_withTransformers() throws { 104 | let response = ResponseDefinition().withTransformers([Transformer.responseTemplate]) 105 | let transformers = try XCTUnwrap(response.asDict()[ResponseDefinition.Constants.keyTransformers] as? [String]) 106 | let firstTransformer = try XCTUnwrap(transformers.first) 107 | XCTAssertEqual(firstTransformer, Transformer.responseTemplate.rawValue) 108 | } 109 | 110 | func test_withFixedDelay() throws { 111 | let response = ResponseDefinition().withFixedDelay(2) 112 | XCTAssertEqual(response.asDict()[ResponseDefinition.Constants.keyFixedDelay] as? Int, 2) 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/StubMappingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StubMappingTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import XCTest 9 | @testable import WiremockClient 10 | 11 | class StubMappingTests: XCTestCase { 12 | 13 | func test_requestMethods() throws { 14 | var mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "") 15 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "ANY") 16 | 17 | mapping = StubMapping.stubFor(requestMethod: .DELETE, urlMatchCondition: .urlEqualTo, url: "") 18 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "DELETE") 19 | 20 | mapping = StubMapping.stubFor(requestMethod: .GET, urlMatchCondition: .urlEqualTo, url: "") 21 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "GET") 22 | 23 | mapping = StubMapping.stubFor(requestMethod: .HEAD, urlMatchCondition: .urlEqualTo, url: "") 24 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "HEAD") 25 | 26 | mapping = StubMapping.stubFor(requestMethod: .OPTIONS, urlMatchCondition: .urlEqualTo, url: "") 27 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "OPTIONS") 28 | 29 | mapping = StubMapping.stubFor(requestMethod: .PATCH, urlMatchCondition: .urlEqualTo, url: "") 30 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "PATCH") 31 | 32 | mapping = StubMapping.stubFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "") 33 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "POST") 34 | 35 | mapping = StubMapping.stubFor(requestMethod: .PUT, urlMatchCondition: .urlEqualTo, url: "") 36 | XCTAssertEqual(mapping.requestValue(for: RequestMapping.Constants.keyMethod), "PUT") 37 | } 38 | 39 | func test_urlMatchCondition() throws { 40 | var path = "test/path?param=0" 41 | var mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: path) 42 | XCTAssertEqual(mapping.requestValue(for: URLMatchCondition.urlEqualTo.rawValue), path) 43 | 44 | mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: path) 45 | XCTAssertEqual(mapping.requestValue(for: URLMatchCondition.urlMatching.rawValue), path) 46 | 47 | path = "test/path" 48 | mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlPathEqualTo, url: path) 49 | XCTAssertEqual(mapping.requestValue(for: URLMatchCondition.urlPathEqualTo.rawValue), path) 50 | 51 | path = "test/.*/path" 52 | mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlPathMatching, url: path) 53 | XCTAssertEqual(mapping.requestValue(for: URLMatchCondition.urlPathMatching.rawValue), path) 54 | } 55 | 56 | func test_withUUID() throws { 57 | let uuid = UUID() 58 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "").withUUID(uuid) 59 | XCTAssertEqual(mapping.topLevelValue(for: StubMapping.Constants.keyUuid), uuid.uuidString) 60 | } 61 | 62 | func test_withPriority() throws { 63 | let priority = 99 64 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlEqualTo, url: "").withPriority(priority) 65 | XCTAssertEqual(mapping.topLevelValue(for: StubMapping.Constants.keyPriority), priority) 66 | } 67 | 68 | func test_inScenario() throws { 69 | let scenarioName = "testScenario" 70 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").inScenario(scenarioName) 71 | XCTAssertEqual(mapping.topLevelValue(for: StubMapping.Constants.keyScenName), scenarioName) 72 | } 73 | 74 | func test_whenScenarioStateIs() throws { 75 | let scenarioState = "testScenarioState" 76 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").whenScenarioStateIs(scenarioState) 77 | XCTAssertEqual(mapping.topLevelValue(for: StubMapping.Constants.keyReqScen), scenarioState) 78 | } 79 | 80 | func test_willSetStateTo() throws { 81 | let newScenarioState = "newScenarioState" 82 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").willSetStateTo(newScenarioState) 83 | XCTAssertEqual(mapping.topLevelValue(for: StubMapping.Constants.keyNewScen), newScenarioState) 84 | } 85 | 86 | func test_withHeader() throws { 87 | let key = "testKey" 88 | let value = "testValue" 89 | MatchCondition.allCases.forEach { condition in 90 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withHeader(key, matchCondition: condition, value: value) 91 | XCTAssertTrue(mapping.verifySubdictionaryMatchCondition(dictionaryName: ResponseDefinition.Constants.keyHeaders, key: key, value: value, matchCondition: condition)) 92 | } 93 | } 94 | 95 | func test_withCookie() throws { 96 | let key = "testKey" 97 | let value = "testValue" 98 | MatchCondition.allCases.forEach { condition in 99 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withCookie(key, matchCondition: condition, value: value) 100 | XCTAssertTrue(mapping.verifySubdictionaryMatchCondition(dictionaryName: RequestMapping.Constants.keyCookies, key: key, value: value, matchCondition: condition)) 101 | } 102 | } 103 | 104 | func test_withQueryParam() throws { 105 | let key = "testKey" 106 | let value = "testValue" 107 | MatchCondition.allCases.forEach { condition in 108 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withQueryParam(key, matchCondition: condition, value: value) 109 | XCTAssertTrue(mapping.verifySubdictionaryMatchCondition(dictionaryName: RequestMapping.Constants.keyParams, key: key, value: value, matchCondition: condition)) 110 | } 111 | } 112 | 113 | func test_withBasicAuth() throws { 114 | let username = "testUsername" 115 | let password = "testPassword" 116 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withBasicAuth(username: username, password: password) 117 | 118 | let requestDict = try XCTUnwrap(mapping.asDict()[StubMapping.Constants.keyRequest] as? [String: Any]) 119 | let authDict = try XCTUnwrap(requestDict[RequestMapping.Constants.keyBasicAuth] as? [String: Any]) 120 | XCTAssertEqual(authDict[RequestMapping.Constants.keyUsername] as? String, username) 121 | XCTAssertEqual(authDict[RequestMapping.Constants.keyPassword] as? String, password) 122 | } 123 | 124 | func test_withRequestBody() throws { 125 | let value = "testValue" 126 | MatchCondition.allCases.forEach { condition in 127 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withRequestBody(condition, value: value) 128 | guard let bodyPatternDict = mapping.getFirstBodyPattern(), 129 | let actualValue = bodyPatternDict[condition.rawValue] as? String else { 130 | return XCTFail() 131 | } 132 | XCTAssertEqual(value, actualValue) 133 | } 134 | } 135 | 136 | func test_withRequestBodyEqualToJson() throws { 137 | let jsonString = "{ \"total_results\": 4 }" 138 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "").withRequestBodyEqualToJson(jsonString: jsonString, ignoreArrayOrder: false, ignoreExtraElements: true) 139 | let bodyPatternDict = try XCTUnwrap(mapping.getFirstBodyPattern()) 140 | let jsonResult = try XCTUnwrap(bodyPatternDict[MatchCondition.equalToJson.rawValue] as? String) 141 | XCTAssertEqual(jsonString, jsonResult) 142 | let ignoreExtraElements = try XCTUnwrap(bodyPatternDict[RequestMapping.Constants.keyIgnoreExtraElements] as? Bool) 143 | XCTAssertTrue(ignoreExtraElements) 144 | } 145 | 146 | func test_withRequestBodyFromLocalJsonFile() throws { 147 | let jsonString = "{\"testKey\":\"testValue\"}" 148 | let fileName = "test" 149 | let mapping = StubMapping.stubFor(requestMethod: .ANY, urlMatchCondition: .urlMatching, url: "") 150 | .withRequestBodyFromLocalJsonFile(fileName, in: Bundle(for: type(of: self)), ignoreArayOrder: true, ignoreExtraElements: true) 151 | let bodyPatternDict = try XCTUnwrap(mapping.getFirstBodyPattern()) 152 | let jsonResult = try XCTUnwrap(bodyPatternDict[MatchCondition.equalToJson.rawValue] as? String) 153 | XCTAssertEqual(jsonString, jsonResult) 154 | let ignoreExtraElements = try XCTUnwrap(bodyPatternDict[RequestMapping.Constants.keyIgnoreExtraElements] as? Bool) 155 | XCTAssertTrue(ignoreExtraElements) 156 | } 157 | } 158 | 159 | private extension StubMapping { 160 | 161 | func topLevelValue(for key: String) -> T? { 162 | return asDict()[key] as? T 163 | } 164 | 165 | func requestValue(for key: String) -> T? { 166 | guard let requestDict = asDict()[StubMapping.Constants.keyRequest] as? [String: Any] else { 167 | return nil 168 | } 169 | return requestDict[key] as? T 170 | } 171 | 172 | func verifySubdictionaryMatchCondition(dictionaryName: String, key: String, value: String, matchCondition: MatchCondition) -> Bool { 173 | guard let requestDict = asDict()[StubMapping.Constants.keyRequest] as? [String: Any], 174 | let subDict = requestDict[dictionaryName] as? [String: Any], 175 | let keyDict = subDict[key] as? [String: Any], 176 | let actualValue = keyDict[matchCondition.rawValue] as? String else { 177 | return false 178 | } 179 | return value == actualValue 180 | } 181 | 182 | func getFirstBodyPattern() -> [String: Any]? { 183 | guard let requestDict = asDict()[StubMapping.Constants.keyRequest] as? [String: Any], 184 | let array = requestDict[RequestMapping.Constants.keyBodyPatterns] as? [[String: Any]] else { 185 | return nil 186 | } 187 | return array.first 188 | } 189 | 190 | func responseValue(for key: String) -> T? { 191 | guard let requestDict = asDict()[StubMapping.Constants.keyResponse] as? [String: Any] else { 192 | return nil 193 | } 194 | return requestDict[key] as? T 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/WiremockClientErrorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClientErrorTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import XCTest 9 | @testable import WiremockClient 10 | 11 | class WiremockClientErrorTests: XCTestCase { 12 | 13 | func test_equatable() throws { 14 | XCTAssertEqual(WiremockClientError.decodingError, WiremockClientError.decodingError) 15 | XCTAssertEqual(WiremockClientError.invalidUrl, WiremockClientError.invalidUrl) 16 | XCTAssertEqual(WiremockClientError.verificationError(description: "desc"), WiremockClientError.verificationError(description: "desc")) 17 | XCTAssertEqual(WiremockClientError.networkError, WiremockClientError.networkError) 18 | XCTAssertNotEqual(WiremockClientError.decodingError, WiremockClientError.invalidUrl) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/WiremockClientNetworkServiceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClientNetworkServiceTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/15/21. 6 | // 7 | 8 | import XCTest 9 | @testable import WiremockClient 10 | 11 | class WiremockClientNetworkServiceTests: XCTestCase { 12 | 13 | private var networkService: WiremockClientNetworkService! 14 | private var urlSession: StubSynchronousURLSession! 15 | 16 | override func setUpWithError() throws { 17 | networkService = WiremockClientNetworkService() 18 | urlSession = StubSynchronousURLSession() 19 | networkService.urlSession = urlSession 20 | } 21 | 22 | override func tearDownWithError() throws { 23 | networkService = nil 24 | urlSession = nil 25 | } 26 | 27 | func test_makeSynchronousRequest_success() throws { 28 | XCTAssertNoThrow(try networkService.makeSynchronousRequest(with: WiremockEndpoint.resetServer)) 29 | } 30 | 31 | func test_makeSynchronousRequest_error() throws { 32 | urlSession.error = WiremockClientError.networkError 33 | XCTAssertThrowsError(try networkService.makeSynchronousRequest(with: TestEndpoint.withoutResult)) 34 | } 35 | 36 | func test_makeSynchronousRequest_invalidUrl() throws { 37 | XCTAssertThrowsError(try networkService.makeSynchronousRequest(with: TestEndpoint.withoutUrlRequest)) { error in 38 | XCTAssertEqual(error as? WiremockClientError, .invalidUrl) 39 | } 40 | } 41 | 42 | func test_makeSynchronousRequest_decodableResult_success() throws { 43 | let value = 99 44 | let result = TestCodable(value: value) 45 | let resultData = try XCTUnwrap(result.asData()) 46 | urlSession.data = resultData 47 | let networkResult: TestCodable = try networkService.makeSynchronousRequest(with: TestEndpoint.withResult(result: result)) 48 | XCTAssertEqual(networkResult.value, value) 49 | } 50 | 51 | func test_makeSynchronousRequest_decodableResult_networkError() throws { 52 | urlSession.error = WiremockClientError.networkError 53 | let result = TestCodable(value: 100) 54 | let throwingClosure: () throws -> Void = { 55 | let _ : TestCodable = try self.networkService.makeSynchronousRequest(with: TestEndpoint.withResult(result: result)) 56 | } 57 | XCTAssertThrowsError(try throwingClosure()) 58 | } 59 | 60 | func test_makeSynchronousRequest_decodableResult_invalidUrl() throws { 61 | let result = TestCodable(value: 100) 62 | let throwingClosure: () throws -> Void = { 63 | let _ : TestCodable = try self.networkService.makeSynchronousRequest(with: TestEndpoint.withResultWithoutUrlRequest(result: result)) 64 | } 65 | XCTAssertThrowsError(try throwingClosure()) { error in 66 | XCTAssertEqual(error as? WiremockClientError, .invalidUrl) 67 | } 68 | } 69 | 70 | func test_makeSynchronousRequest_decodableResult_noData() throws { 71 | let result = TestCodable(value: 100) 72 | let throwingClosure: () throws -> Void = { 73 | let _ : TestCodable = try self.networkService.makeSynchronousRequest(with: TestEndpoint.withResult(result: result)) 74 | } 75 | XCTAssertThrowsError(try throwingClosure()) { error in 76 | XCTAssertEqual(error as? WiremockClientError, .decodingError) 77 | } 78 | } 79 | 80 | } 81 | 82 | private class StubSynchronousURLSession: SynchronousURLSession { 83 | 84 | var data: Data? 85 | var error: Error? 86 | 87 | func executeSynchronousRequest(_ request: URLRequest) -> Result { 88 | if let error = error { 89 | return .failure(error) 90 | } else { 91 | return .success(data) 92 | } 93 | } 94 | 95 | } 96 | 97 | private enum TestEndpoint: Endpoint { 98 | case withoutResult 99 | case withResult(result: TestCodable) 100 | case withoutUrlRequest 101 | case withResultWithoutUrlRequest(result: TestCodable) 102 | 103 | var path: String { 104 | return "path" 105 | } 106 | 107 | var requestMethod: RequestMethod { 108 | return .POST 109 | } 110 | 111 | var body: Data? { 112 | return nil 113 | } 114 | 115 | var urlRequest: URLRequest? { 116 | switch self { 117 | case .withoutUrlRequest, 118 | .withResultWithoutUrlRequest: 119 | return nil 120 | default: 121 | guard let url = URL(string: "http://localhost:8080") else { return nil } 122 | return URLRequest(url: url) 123 | } 124 | } 125 | 126 | } 127 | 128 | private struct TestCodable: Codable { 129 | var value: Int 130 | 131 | func asData() -> Data? { 132 | return try? JSONSerialization.data(withJSONObject: ["value": value], options: [.prettyPrinted]) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /Tests/WiremockClientTests/WiremockClientTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WiremockClientTests.swift 3 | // WiremockClientTests 4 | // 5 | // Created by Ted Rothrock on 2/12/21. 6 | // 7 | 8 | import XCTest 9 | @testable import WiremockClient 10 | 11 | class WiremockClientTests: XCTestCase { 12 | 13 | private var networkService: MockNetworkService! 14 | 15 | override func setUpWithError() throws { 16 | networkService = MockNetworkService() 17 | WiremockClient.networkService = networkService 18 | } 19 | 20 | override func tearDownWithError() throws { 21 | networkService = nil 22 | } 23 | 24 | private var stubLoggedRequests: [LoggedRequest] = { 25 | return [ 26 | LoggedRequest(url: "/my/test/url", 27 | absoluteUrl: "http://localhost:8080/my/test/url", 28 | method: .POST, 29 | body: "Test body", 30 | browserProxyRequest: false, 31 | loggedDateString: "2012-06-07 16:39:41", 32 | headers: ["Accept": "text/plain", "Content-Type": "text/plain"]) 33 | ] 34 | }() 35 | 36 | func test_postMapping() throws { 37 | try WiremockClient.postMapping(stubMapping: StubMapping.stubFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/test/path").willReturn(ResponseDefinition().withStatus(200))) 38 | 39 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 40 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/mappings") 41 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 42 | } 43 | 44 | func test_updateMapping() throws { 45 | let uuid = UUID() 46 | try WiremockClient.updateMapping(uuid: uuid, stubMapping: StubMapping.stubFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost:8080/test/path")) 47 | 48 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "PUT") 49 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/mappings/\(uuid.uuidString)") 50 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 51 | } 52 | 53 | func test_deleteMapping() throws { 54 | let uuid = UUID() 55 | try WiremockClient.deleteMapping(uuid: uuid) 56 | 57 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "DELETE") 58 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/mappings/\(uuid.uuidString)") 59 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 60 | } 61 | 62 | func test_saveAllMappings() throws { 63 | try WiremockClient.saveAllMappings() 64 | 65 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 66 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/mappings/save") 67 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 68 | } 69 | 70 | func test_findRequests() throws { 71 | networkService.decodableResponse = LoggedRequestResponse(requests: stubLoggedRequests) 72 | let _ = try WiremockClient.findRequests(requestMapping: RequestMapping.requestFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost/test/request")) 73 | 74 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 75 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/requests/find") 76 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 77 | } 78 | 79 | func test_deleteAllRequests() throws { 80 | try WiremockClient.deleteAllRequests() 81 | 82 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "DELETE") 83 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/requests") 84 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 85 | } 86 | 87 | func test_verifyRequestMapping_success() throws { 88 | networkService.decodableResponse = LoggedRequestResponse(requests: stubLoggedRequests) 89 | try WiremockClient.verify(requestMapping: RequestMapping.requestFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost/test/request")) 90 | 91 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 92 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/requests/find") 93 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 94 | } 95 | 96 | func test_verifyRequestMapping_failure() throws { 97 | networkService.decodableResponse = LoggedRequestResponse(requests: []) 98 | XCTAssertThrowsError(try WiremockClient.verify(requestMapping: RequestMapping.requestFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost/test/request"))) { error in 99 | let wiremockClientError = try? XCTUnwrap(error as? WiremockClientError) 100 | guard case .verificationError = wiremockClientError else { 101 | return XCTFail() 102 | } 103 | } 104 | } 105 | 106 | func test_verifyRequestMappingExpectedCount_success() throws { 107 | networkService.decodableResponse = LoggedRequestResponse(requests: stubLoggedRequests) 108 | try WiremockClient.verify(expectedCount: 1, requestMapping: RequestMapping.requestFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost/test/request")) 109 | 110 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 111 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/requests/find") 112 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 113 | } 114 | 115 | func test_verifyRequestMappingExpectedCount_failure() throws { 116 | networkService.decodableResponse = LoggedRequestResponse(requests: []) 117 | XCTAssertThrowsError(try WiremockClient.verify(expectedCount: 1, requestMapping: RequestMapping.requestFor(requestMethod: .POST, urlMatchCondition: .urlEqualTo, url: "http://localhost/test/request"))) { error in 118 | let wiremockClientError = try? XCTUnwrap(error as? WiremockClientError) 119 | guard case .verificationError = wiremockClientError else { 120 | return XCTFail() 121 | } 122 | } 123 | } 124 | 125 | func test_isServerRunning_success() throws { 126 | networkService.decodableResponse = MappingsResponse(total: 1) 127 | XCTAssertTrue(try WiremockClient.isServerRunning()) 128 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "GET") 129 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/mappings") 130 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 131 | } 132 | 133 | func test_isServerRunning_failure() throws { 134 | XCTAssertThrowsError(try WiremockClient.isServerRunning()) { error in 135 | let wiremockClientError = try? XCTUnwrap(error as? WiremockClientError) 136 | guard case .decodingError = wiremockClientError else { 137 | return XCTFail() 138 | } 139 | } 140 | } 141 | 142 | func test_resetServer() throws { 143 | try WiremockClient.reset() 144 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 145 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/reset") 146 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 147 | } 148 | 149 | func test_resetAllScenarios() throws { 150 | try WiremockClient.resetAllScenarios() 151 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 152 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/scenarios/reset") 153 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 154 | } 155 | 156 | func test_shutDownServer() throws { 157 | try WiremockClient.shutdownServer() 158 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 159 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/shutdown") 160 | XCTAssertNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 161 | } 162 | 163 | func test_setGlobalDelay() throws { 164 | try WiremockClient.setGlobalDelay(2000) 165 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.httpMethod, "POST") 166 | XCTAssertEqual(networkService.cachedEndpoint?.urlRequest?.url?.absoluteString, "http://localhost:8080/__admin/settings") 167 | XCTAssertNotNil(networkService.cachedEndpoint?.urlRequest?.httpBody) 168 | } 169 | 170 | } 171 | 172 | private class MockNetworkService: NetworkService { 173 | 174 | var cachedEndpoint: Endpoint? 175 | var decodableResponse: Decodable? 176 | 177 | func makeSynchronousRequest(with endpoint: Endpoint) throws { 178 | cachedEndpoint = endpoint 179 | } 180 | 181 | func makeSynchronousRequest(with endpoint: Endpoint) throws -> T { 182 | guard let decodableResponse = decodableResponse as? T else { 183 | throw WiremockClientError.decodingError 184 | } 185 | cachedEndpoint = endpoint 186 | return decodableResponse 187 | } 188 | 189 | } 190 | -------------------------------------------------------------------------------- /WiremockClient.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'WiremockClient' 3 | s.version = '3.0.0' 4 | s.summary = 'An HTTP client for Wiremock standalone instances' 5 | 6 | s.description = <<-DESC 7 | WiremockClient is an HTTP client that allows users to interact with a standalone Wiremock instance from within an Xcode project. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/mobileforming/WiremockClient' 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { 'Ted Rothrock' => 'theodore.rothrock@gmail.com' } 13 | s.source = { :git => 'https://github.com/mobileforming/WiremockClient.git', :tag => '3.0.0' } 14 | s.ios.deployment_target = '14.0' 15 | s.osx.deployment_target = '11.0' 16 | s.swift_versions = ['4.0', '5.0'] 17 | 18 | s.source_files = 'Sources/**/*' 19 | end 20 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/WiremockClientTests_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | BNDL 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/WiremockClient_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXAggregateTarget section */ 10 | "WiremockClient::WiremockClientPackageTests::ProductTarget" /* WiremockClientPackageTests */ = { 11 | isa = PBXAggregateTarget; 12 | buildConfigurationList = OBJ_49 /* Build configuration list for PBXAggregateTarget "WiremockClientPackageTests" */; 13 | buildPhases = ( 14 | ); 15 | dependencies = ( 16 | OBJ_52 /* PBXTargetDependency */, 17 | ); 18 | name = WiremockClientPackageTests; 19 | productName = WiremockClientPackageTests; 20 | }; 21 | /* End PBXAggregateTarget section */ 22 | 23 | /* Begin PBXBuildFile section */ 24 | 597918602188FEF900D557EE /* RequestMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5979185E2188FEEF00D557EE /* RequestMapping.swift */; }; 25 | 9DA6926022E0F7BA002F014E /* Transformer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DA6925E22E0F78C002F014E /* Transformer.swift */; }; 26 | C2253C3325E609E000154E6C /* IntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2253C3225E609E000154E6C /* IntegrationTests.swift */; }; 27 | C2253C3D25E6162900154E6C /* LoggedRequestResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2253C3C25E6162900154E6C /* LoggedRequestResponse.swift */; }; 28 | C24CB78825DAFD1F007E6BAB /* MappingsResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB78625DAFD1F007E6BAB /* MappingsResponse.swift */; }; 29 | C24CB79625DB0075007E6BAB /* StubMappingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB79525DB0075007E6BAB /* StubMappingTests.swift */; }; 30 | C24CB7AF25DB21AD007E6BAB /* MatchCondition.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB7AE25DB21AD007E6BAB /* MatchCondition.swift */; }; 31 | C24CB7B925DB2295007E6BAB /* ResponseDefinitionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB7B825DB2295007E6BAB /* ResponseDefinitionTests.swift */; }; 32 | C24CB7EA25DB3416007E6BAB /* test.json in Resources */ = {isa = PBXBuildFile; fileRef = C24CB7E825DB33FF007E6BAB /* test.json */; }; 33 | C24CB7F825DB3789007E6BAB /* WiremockClientErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB7F725DB3789007E6BAB /* WiremockClientErrorTests.swift */; }; 34 | C24CB80225DB38D5007E6BAB /* WiremockClientNetworkServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB80125DB38D5007E6BAB /* WiremockClientNetworkServiceTests.swift */; }; 35 | C24CB81025DB4CB3007E6BAB /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = C24CB80F25DB4CB3007E6BAB /* Endpoint.swift */; }; 36 | C2BBFD4B25D6609B00217D10 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFD3F25D6605200217D10 /* NetworkService.swift */; }; 37 | C2BBFD5025D6609E00217D10 /* WiremockClientNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFD4525D6607800217D10 /* WiremockClientNetworkService.swift */; }; 38 | C2BBFD5725D662CB00217D10 /* DebugOverridable.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFD5525D662CB00217D10 /* DebugOverridable.swift */; }; 39 | C2BBFD6725D664AC00217D10 /* WiremockClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFD6625D664AC00217D10 /* WiremockClientTests.swift */; }; 40 | C2BBFD9C25D66F7700217D10 /* WiremockClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFD9A25D66F7700217D10 /* WiremockClientError.swift */; }; 41 | C2BBFDA725D676D100217D10 /* LoggedRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFDA525D676D100217D10 /* LoggedRequest.swift */; }; 42 | C2BBFDB225DAE6A000217D10 /* WiremockEndpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBFDB025DAE6A000217D10 /* WiremockEndpoint.swift */; }; 43 | OBJ_26 /* Package.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_6 /* Package.swift */; }; 44 | OBJ_34 /* WiremockClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = "WiremockClient::WiremockClient::Product" /* WiremockClient.framework */; }; 45 | OBJ_41 /* RequestMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_9 /* RequestMethod.swift */; }; 46 | OBJ_42 /* RequestPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_10 /* RequestPattern.swift */; }; 47 | OBJ_43 /* ResponseDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_11 /* ResponseDefinition.swift */; }; 48 | OBJ_44 /* StubMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* StubMapping.swift */; }; 49 | OBJ_45 /* UrlPattern.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* UrlPattern.swift */; }; 50 | OBJ_46 /* WiremockClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* WiremockClient.swift */; }; 51 | /* End PBXBuildFile section */ 52 | 53 | /* Begin PBXContainerItemProxy section */ 54 | 5A37511A2044D693002B7B44 /* PBXContainerItemProxy */ = { 55 | isa = PBXContainerItemProxy; 56 | containerPortal = OBJ_1 /* Project object */; 57 | proxyType = 1; 58 | remoteGlobalIDString = "WiremockClient::WiremockClient"; 59 | remoteInfo = WiremockClient; 60 | }; 61 | 5A37511B2044D693002B7B44 /* PBXContainerItemProxy */ = { 62 | isa = PBXContainerItemProxy; 63 | containerPortal = OBJ_1 /* Project object */; 64 | proxyType = 1; 65 | remoteGlobalIDString = "WiremockClient::WiremockClientTests"; 66 | remoteInfo = WiremockClientTests; 67 | }; 68 | /* End PBXContainerItemProxy section */ 69 | 70 | /* Begin PBXFileReference section */ 71 | 5979185E2188FEEF00D557EE /* RequestMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestMapping.swift; sourceTree = ""; }; 72 | 9DA6925E22E0F78C002F014E /* Transformer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformer.swift; sourceTree = ""; }; 73 | C2253C3225E609E000154E6C /* IntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IntegrationTests.swift; sourceTree = ""; }; 74 | C2253C3C25E6162900154E6C /* LoggedRequestResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedRequestResponse.swift; sourceTree = ""; }; 75 | C24CB78625DAFD1F007E6BAB /* MappingsResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MappingsResponse.swift; sourceTree = ""; }; 76 | C24CB79525DB0075007E6BAB /* StubMappingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubMappingTests.swift; sourceTree = ""; }; 77 | C24CB7AE25DB21AD007E6BAB /* MatchCondition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatchCondition.swift; sourceTree = ""; }; 78 | C24CB7B825DB2295007E6BAB /* ResponseDefinitionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseDefinitionTests.swift; sourceTree = ""; }; 79 | C24CB7E825DB33FF007E6BAB /* test.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = test.json; sourceTree = ""; }; 80 | C24CB7F725DB3789007E6BAB /* WiremockClientErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClientErrorTests.swift; sourceTree = ""; }; 81 | C24CB80125DB38D5007E6BAB /* WiremockClientNetworkServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClientNetworkServiceTests.swift; sourceTree = ""; }; 82 | C24CB80F25DB4CB3007E6BAB /* Endpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Endpoint.swift; sourceTree = ""; }; 83 | C2BBFD3F25D6605200217D10 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; }; 84 | C2BBFD4525D6607800217D10 /* WiremockClientNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClientNetworkService.swift; sourceTree = ""; }; 85 | C2BBFD5525D662CB00217D10 /* DebugOverridable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugOverridable.swift; sourceTree = ""; }; 86 | C2BBFD6625D664AC00217D10 /* WiremockClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClientTests.swift; sourceTree = ""; }; 87 | C2BBFD9A25D66F7700217D10 /* WiremockClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClientError.swift; sourceTree = ""; }; 88 | C2BBFDA525D676D100217D10 /* LoggedRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggedRequest.swift; sourceTree = ""; }; 89 | C2BBFDB025DAE6A000217D10 /* WiremockEndpoint.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockEndpoint.swift; sourceTree = ""; }; 90 | OBJ_10 /* RequestPattern.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestPattern.swift; sourceTree = ""; }; 91 | OBJ_11 /* ResponseDefinition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResponseDefinition.swift; sourceTree = ""; }; 92 | OBJ_12 /* StubMapping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StubMapping.swift; sourceTree = ""; }; 93 | OBJ_13 /* UrlPattern.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UrlPattern.swift; sourceTree = ""; }; 94 | OBJ_14 /* WiremockClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WiremockClient.swift; sourceTree = ""; }; 95 | OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; 96 | OBJ_9 /* RequestMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestMethod.swift; sourceTree = ""; }; 97 | "WiremockClient::WiremockClient::Product" /* WiremockClient.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = WiremockClient.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 98 | "WiremockClient::WiremockClientTests::Product" /* WiremockClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = WiremockClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 99 | /* End PBXFileReference section */ 100 | 101 | /* Begin PBXFrameworksBuildPhase section */ 102 | OBJ_33 /* Frameworks */ = { 103 | isa = PBXFrameworksBuildPhase; 104 | buildActionMask = 0; 105 | files = ( 106 | OBJ_34 /* WiremockClient.framework in Frameworks */, 107 | ); 108 | runOnlyForDeploymentPostprocessing = 0; 109 | }; 110 | OBJ_47 /* Frameworks */ = { 111 | isa = PBXFrameworksBuildPhase; 112 | buildActionMask = 0; 113 | files = ( 114 | ); 115 | runOnlyForDeploymentPostprocessing = 0; 116 | }; 117 | /* End PBXFrameworksBuildPhase section */ 118 | 119 | /* Begin PBXGroup section */ 120 | C24CB77D25DAFC5E007E6BAB /* ResponseObjects */ = { 121 | isa = PBXGroup; 122 | children = ( 123 | C2253C3C25E6162900154E6C /* LoggedRequestResponse.swift */, 124 | C2BBFDA525D676D100217D10 /* LoggedRequest.swift */, 125 | C24CB78625DAFD1F007E6BAB /* MappingsResponse.swift */, 126 | ); 127 | path = ResponseObjects; 128 | sourceTree = ""; 129 | }; 130 | C24CB77E25DAFC6D007E6BAB /* Networking */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | C24CB80F25DB4CB3007E6BAB /* Endpoint.swift */, 134 | C2BBFD3F25D6605200217D10 /* NetworkService.swift */, 135 | OBJ_9 /* RequestMethod.swift */, 136 | C2BBFD4525D6607800217D10 /* WiremockClientNetworkService.swift */, 137 | C2BBFDB025DAE6A000217D10 /* WiremockEndpoint.swift */, 138 | C24CB77D25DAFC5E007E6BAB /* ResponseObjects */, 139 | ); 140 | path = Networking; 141 | sourceTree = ""; 142 | }; 143 | C24CB77F25DAFC88007E6BAB /* Utils */ = { 144 | isa = PBXGroup; 145 | children = ( 146 | C2BBFD5525D662CB00217D10 /* DebugOverridable.swift */, 147 | ); 148 | path = Utils; 149 | sourceTree = ""; 150 | }; 151 | C24CB78025DAFCBA007E6BAB /* Stubbing */ = { 152 | isa = PBXGroup; 153 | children = ( 154 | C24CB7AE25DB21AD007E6BAB /* MatchCondition.swift */, 155 | 5979185E2188FEEF00D557EE /* RequestMapping.swift */, 156 | OBJ_10 /* RequestPattern.swift */, 157 | OBJ_11 /* ResponseDefinition.swift */, 158 | OBJ_12 /* StubMapping.swift */, 159 | 9DA6925E22E0F78C002F014E /* Transformer.swift */, 160 | OBJ_13 /* UrlPattern.swift */, 161 | ); 162 | path = Stubbing; 163 | sourceTree = ""; 164 | }; 165 | C24CB7E725DB33EE007E6BAB /* JSON */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | C24CB7E825DB33FF007E6BAB /* test.json */, 169 | ); 170 | path = JSON; 171 | sourceTree = ""; 172 | }; 173 | OBJ_15 /* Tests */ = { 174 | isa = PBXGroup; 175 | children = ( 176 | OBJ_16 /* WiremockClientTests */, 177 | ); 178 | name = Tests; 179 | sourceTree = SOURCE_ROOT; 180 | }; 181 | OBJ_16 /* WiremockClientTests */ = { 182 | isa = PBXGroup; 183 | children = ( 184 | C2253C3225E609E000154E6C /* IntegrationTests.swift */, 185 | C24CB7B825DB2295007E6BAB /* ResponseDefinitionTests.swift */, 186 | C24CB79525DB0075007E6BAB /* StubMappingTests.swift */, 187 | C24CB7F725DB3789007E6BAB /* WiremockClientErrorTests.swift */, 188 | C24CB80125DB38D5007E6BAB /* WiremockClientNetworkServiceTests.swift */, 189 | C2BBFD6625D664AC00217D10 /* WiremockClientTests.swift */, 190 | C24CB7E725DB33EE007E6BAB /* JSON */, 191 | ); 192 | name = WiremockClientTests; 193 | path = Tests/WiremockClientTests; 194 | sourceTree = SOURCE_ROOT; 195 | }; 196 | OBJ_18 /* Products */ = { 197 | isa = PBXGroup; 198 | children = ( 199 | "WiremockClient::WiremockClientTests::Product" /* WiremockClientTests.xctest */, 200 | "WiremockClient::WiremockClient::Product" /* WiremockClient.framework */, 201 | ); 202 | name = Products; 203 | sourceTree = BUILT_PRODUCTS_DIR; 204 | }; 205 | OBJ_5 = { 206 | isa = PBXGroup; 207 | children = ( 208 | OBJ_6 /* Package.swift */, 209 | OBJ_7 /* Sources */, 210 | OBJ_15 /* Tests */, 211 | OBJ_18 /* Products */, 212 | ); 213 | sourceTree = ""; 214 | }; 215 | OBJ_7 /* Sources */ = { 216 | isa = PBXGroup; 217 | children = ( 218 | OBJ_8 /* WiremockClient */, 219 | ); 220 | name = Sources; 221 | sourceTree = SOURCE_ROOT; 222 | }; 223 | OBJ_8 /* WiremockClient */ = { 224 | isa = PBXGroup; 225 | children = ( 226 | OBJ_14 /* WiremockClient.swift */, 227 | C2BBFD9A25D66F7700217D10 /* WiremockClientError.swift */, 228 | C24CB77E25DAFC6D007E6BAB /* Networking */, 229 | C24CB78025DAFCBA007E6BAB /* Stubbing */, 230 | C24CB77F25DAFC88007E6BAB /* Utils */, 231 | ); 232 | name = WiremockClient; 233 | path = Sources/WiremockClient; 234 | sourceTree = SOURCE_ROOT; 235 | }; 236 | /* End PBXGroup section */ 237 | 238 | /* Begin PBXNativeTarget section */ 239 | "WiremockClient::SwiftPMPackageDescription" /* WiremockClientPackageDescription */ = { 240 | isa = PBXNativeTarget; 241 | buildConfigurationList = OBJ_22 /* Build configuration list for PBXNativeTarget "WiremockClientPackageDescription" */; 242 | buildPhases = ( 243 | OBJ_25 /* Sources */, 244 | ); 245 | buildRules = ( 246 | ); 247 | dependencies = ( 248 | ); 249 | name = WiremockClientPackageDescription; 250 | productName = WiremockClientPackageDescription; 251 | productType = "com.apple.product-type.framework"; 252 | }; 253 | "WiremockClient::WiremockClient" /* WiremockClient */ = { 254 | isa = PBXNativeTarget; 255 | buildConfigurationList = OBJ_37 /* Build configuration list for PBXNativeTarget "WiremockClient" */; 256 | buildPhases = ( 257 | OBJ_40 /* Sources */, 258 | OBJ_47 /* Frameworks */, 259 | ); 260 | buildRules = ( 261 | ); 262 | dependencies = ( 263 | ); 264 | name = WiremockClient; 265 | productName = WiremockClient; 266 | productReference = "WiremockClient::WiremockClient::Product" /* WiremockClient.framework */; 267 | productType = "com.apple.product-type.framework"; 268 | }; 269 | "WiremockClient::WiremockClientTests" /* WiremockClientTests */ = { 270 | isa = PBXNativeTarget; 271 | buildConfigurationList = OBJ_28 /* Build configuration list for PBXNativeTarget "WiremockClientTests" */; 272 | buildPhases = ( 273 | OBJ_31 /* Sources */, 274 | OBJ_33 /* Frameworks */, 275 | C24CB7E925DB340F007E6BAB /* Resources */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | OBJ_35 /* PBXTargetDependency */, 281 | ); 282 | name = WiremockClientTests; 283 | productName = WiremockClientTests; 284 | productReference = "WiremockClient::WiremockClientTests::Product" /* WiremockClientTests.xctest */; 285 | productType = "com.apple.product-type.bundle.unit-test"; 286 | }; 287 | /* End PBXNativeTarget section */ 288 | 289 | /* Begin PBXProject section */ 290 | OBJ_1 /* Project object */ = { 291 | isa = PBXProject; 292 | attributes = { 293 | LastUpgradeCheck = 9999; 294 | TargetAttributes = { 295 | "WiremockClient::WiremockClient" = { 296 | LastSwiftMigration = 1230; 297 | }; 298 | "WiremockClient::WiremockClientTests" = { 299 | LastSwiftMigration = 1230; 300 | }; 301 | }; 302 | }; 303 | buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "WiremockClient" */; 304 | compatibilityVersion = "Xcode 3.2"; 305 | developmentRegion = English; 306 | hasScannedForEncodings = 0; 307 | knownRegions = ( 308 | English, 309 | en, 310 | ); 311 | mainGroup = OBJ_5; 312 | productRefGroup = OBJ_18 /* Products */; 313 | projectDirPath = ""; 314 | projectRoot = ""; 315 | targets = ( 316 | "WiremockClient::SwiftPMPackageDescription" /* WiremockClientPackageDescription */, 317 | "WiremockClient::WiremockClientTests" /* WiremockClientTests */, 318 | "WiremockClient::WiremockClient" /* WiremockClient */, 319 | "WiremockClient::WiremockClientPackageTests::ProductTarget" /* WiremockClientPackageTests */, 320 | ); 321 | }; 322 | /* End PBXProject section */ 323 | 324 | /* Begin PBXResourcesBuildPhase section */ 325 | C24CB7E925DB340F007E6BAB /* Resources */ = { 326 | isa = PBXResourcesBuildPhase; 327 | buildActionMask = 2147483647; 328 | files = ( 329 | C24CB7EA25DB3416007E6BAB /* test.json in Resources */, 330 | ); 331 | runOnlyForDeploymentPostprocessing = 0; 332 | }; 333 | /* End PBXResourcesBuildPhase section */ 334 | 335 | /* Begin PBXSourcesBuildPhase section */ 336 | OBJ_25 /* Sources */ = { 337 | isa = PBXSourcesBuildPhase; 338 | buildActionMask = 0; 339 | files = ( 340 | OBJ_26 /* Package.swift in Sources */, 341 | ); 342 | runOnlyForDeploymentPostprocessing = 0; 343 | }; 344 | OBJ_31 /* Sources */ = { 345 | isa = PBXSourcesBuildPhase; 346 | buildActionMask = 0; 347 | files = ( 348 | C24CB79625DB0075007E6BAB /* StubMappingTests.swift in Sources */, 349 | C24CB80225DB38D5007E6BAB /* WiremockClientNetworkServiceTests.swift in Sources */, 350 | C2253C3325E609E000154E6C /* IntegrationTests.swift in Sources */, 351 | C2BBFD6725D664AC00217D10 /* WiremockClientTests.swift in Sources */, 352 | C24CB7F825DB3789007E6BAB /* WiremockClientErrorTests.swift in Sources */, 353 | C24CB7B925DB2295007E6BAB /* ResponseDefinitionTests.swift in Sources */, 354 | ); 355 | runOnlyForDeploymentPostprocessing = 0; 356 | }; 357 | OBJ_40 /* Sources */ = { 358 | isa = PBXSourcesBuildPhase; 359 | buildActionMask = 0; 360 | files = ( 361 | C24CB78825DAFD1F007E6BAB /* MappingsResponse.swift in Sources */, 362 | C2BBFDB225DAE6A000217D10 /* WiremockEndpoint.swift in Sources */, 363 | OBJ_41 /* RequestMethod.swift in Sources */, 364 | C24CB7AF25DB21AD007E6BAB /* MatchCondition.swift in Sources */, 365 | OBJ_42 /* RequestPattern.swift in Sources */, 366 | C2BBFD9C25D66F7700217D10 /* WiremockClientError.swift in Sources */, 367 | C2BBFD5725D662CB00217D10 /* DebugOverridable.swift in Sources */, 368 | C2BBFD5025D6609E00217D10 /* WiremockClientNetworkService.swift in Sources */, 369 | C2BBFDA725D676D100217D10 /* LoggedRequest.swift in Sources */, 370 | OBJ_43 /* ResponseDefinition.swift in Sources */, 371 | 597918602188FEF900D557EE /* RequestMapping.swift in Sources */, 372 | C2BBFD4B25D6609B00217D10 /* NetworkService.swift in Sources */, 373 | 9DA6926022E0F7BA002F014E /* Transformer.swift in Sources */, 374 | OBJ_44 /* StubMapping.swift in Sources */, 375 | OBJ_45 /* UrlPattern.swift in Sources */, 376 | C24CB81025DB4CB3007E6BAB /* Endpoint.swift in Sources */, 377 | C2253C3D25E6162900154E6C /* LoggedRequestResponse.swift in Sources */, 378 | OBJ_46 /* WiremockClient.swift in Sources */, 379 | ); 380 | runOnlyForDeploymentPostprocessing = 0; 381 | }; 382 | /* End PBXSourcesBuildPhase section */ 383 | 384 | /* Begin PBXTargetDependency section */ 385 | OBJ_35 /* PBXTargetDependency */ = { 386 | isa = PBXTargetDependency; 387 | target = "WiremockClient::WiremockClient" /* WiremockClient */; 388 | targetProxy = 5A37511A2044D693002B7B44 /* PBXContainerItemProxy */; 389 | }; 390 | OBJ_52 /* PBXTargetDependency */ = { 391 | isa = PBXTargetDependency; 392 | target = "WiremockClient::WiremockClientTests" /* WiremockClientTests */; 393 | targetProxy = 5A37511B2044D693002B7B44 /* PBXContainerItemProxy */; 394 | }; 395 | /* End PBXTargetDependency section */ 396 | 397 | /* Begin XCBuildConfiguration section */ 398 | OBJ_23 /* Debug */ = { 399 | isa = XCBuildConfiguration; 400 | buildSettings = { 401 | LD = /usr/bin/true; 402 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 403 | SWIFT_VERSION = 4.0; 404 | }; 405 | name = Debug; 406 | }; 407 | OBJ_24 /* Release */ = { 408 | isa = XCBuildConfiguration; 409 | buildSettings = { 410 | LD = /usr/bin/true; 411 | OTHER_SWIFT_FLAGS = "-swift-version 4 -I /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/pm/4 -target x86_64-apple-macosx10.10 -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk"; 412 | SWIFT_VERSION = 4.0; 413 | }; 414 | name = Release; 415 | }; 416 | OBJ_29 /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | buildSettings = { 419 | CLANG_ENABLE_MODULES = YES; 420 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 421 | FRAMEWORK_SEARCH_PATHS = ( 422 | "$(inherited)", 423 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 424 | ); 425 | HEADER_SEARCH_PATHS = "$(inherited)"; 426 | INFOPLIST_FILE = WiremockClient.xcodeproj/WiremockClientTests_Info.plist; 427 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; 428 | OTHER_LDFLAGS = "$(inherited)"; 429 | OTHER_SWIFT_FLAGS = "$(inherited)"; 430 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 431 | SWIFT_VERSION = 5.0; 432 | TARGET_NAME = WiremockClientTests; 433 | }; 434 | name = Debug; 435 | }; 436 | OBJ_3 /* Debug */ = { 437 | isa = XCBuildConfiguration; 438 | buildSettings = { 439 | CLANG_ENABLE_OBJC_ARC = YES; 440 | COMBINE_HIDPI_IMAGES = YES; 441 | COPY_PHASE_STRIP = NO; 442 | DEBUG_INFORMATION_FORMAT = dwarf; 443 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 444 | ENABLE_NS_ASSERTIONS = YES; 445 | GCC_OPTIMIZATION_LEVEL = 0; 446 | GENERATE_INFOPLIST_FILE = YES; 447 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 448 | MACOSX_DEPLOYMENT_TARGET = 11.0; 449 | ONLY_ACTIVE_ARCH = YES; 450 | OTHER_SWIFT_FLAGS = "-DXcode"; 451 | "OTHER_SWIFT_FLAGS[arch=*]" = "-D DEBUG"; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | SDKROOT = macosx; 454 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 455 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 456 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 457 | TVOS_DEPLOYMENT_TARGET = 10.0; 458 | USE_HEADERMAP = NO; 459 | }; 460 | name = Debug; 461 | }; 462 | OBJ_30 /* Release */ = { 463 | isa = XCBuildConfiguration; 464 | buildSettings = { 465 | CLANG_ENABLE_MODULES = YES; 466 | EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; 467 | FRAMEWORK_SEARCH_PATHS = ( 468 | "$(inherited)", 469 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 470 | ); 471 | HEADER_SEARCH_PATHS = "$(inherited)"; 472 | INFOPLIST_FILE = WiremockClient.xcodeproj/WiremockClientTests_Info.plist; 473 | LD_RUNPATH_SEARCH_PATHS = "@loader_path/../Frameworks @loader_path/Frameworks"; 474 | OTHER_LDFLAGS = "$(inherited)"; 475 | OTHER_SWIFT_FLAGS = "$(inherited)"; 476 | SWIFT_VERSION = 5.0; 477 | TARGET_NAME = WiremockClientTests; 478 | }; 479 | name = Release; 480 | }; 481 | OBJ_38 /* Debug */ = { 482 | isa = XCBuildConfiguration; 483 | buildSettings = { 484 | ENABLE_TESTABILITY = YES; 485 | FRAMEWORK_SEARCH_PATHS = ( 486 | "$(inherited)", 487 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 488 | ); 489 | HEADER_SEARCH_PATHS = "$(inherited)"; 490 | INFOPLIST_FILE = WiremockClient.xcodeproj/WiremockClient_Info.plist; 491 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 492 | MARKETING_VERSION = 3.0.0; 493 | OTHER_LDFLAGS = "$(inherited)"; 494 | OTHER_SWIFT_FLAGS = "$(inherited)"; 495 | PRODUCT_BUNDLE_IDENTIFIER = WiremockClient; 496 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 497 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 498 | SKIP_INSTALL = YES; 499 | SWIFT_VERSION = 5.0; 500 | TARGET_NAME = WiremockClient; 501 | }; 502 | name = Debug; 503 | }; 504 | OBJ_39 /* Release */ = { 505 | isa = XCBuildConfiguration; 506 | buildSettings = { 507 | ENABLE_TESTABILITY = YES; 508 | FRAMEWORK_SEARCH_PATHS = ( 509 | "$(inherited)", 510 | "$(PLATFORM_DIR)/Developer/Library/Frameworks", 511 | ); 512 | HEADER_SEARCH_PATHS = "$(inherited)"; 513 | INFOPLIST_FILE = WiremockClient.xcodeproj/WiremockClient_Info.plist; 514 | LD_RUNPATH_SEARCH_PATHS = "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx"; 515 | MARKETING_VERSION = 3.0.0; 516 | OTHER_LDFLAGS = "$(inherited)"; 517 | OTHER_SWIFT_FLAGS = "$(inherited)"; 518 | PRODUCT_BUNDLE_IDENTIFIER = WiremockClient; 519 | PRODUCT_MODULE_NAME = "$(TARGET_NAME:c99extidentifier)"; 520 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 521 | SKIP_INSTALL = YES; 522 | SWIFT_VERSION = 5.0; 523 | TARGET_NAME = WiremockClient; 524 | }; 525 | name = Release; 526 | }; 527 | OBJ_4 /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | buildSettings = { 530 | CLANG_ENABLE_OBJC_ARC = YES; 531 | COMBINE_HIDPI_IMAGES = YES; 532 | COPY_PHASE_STRIP = YES; 533 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 534 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 535 | GCC_OPTIMIZATION_LEVEL = s; 536 | GENERATE_INFOPLIST_FILE = YES; 537 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 538 | MACOSX_DEPLOYMENT_TARGET = 11.0; 539 | OTHER_SWIFT_FLAGS = "-DXcode"; 540 | PRODUCT_NAME = "$(TARGET_NAME)"; 541 | SDKROOT = macosx; 542 | SUPPORTED_PLATFORMS = "macosx iphoneos iphonesimulator appletvos appletvsimulator watchos watchsimulator"; 543 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; 544 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 545 | TVOS_DEPLOYMENT_TARGET = 10.0; 546 | USE_HEADERMAP = NO; 547 | }; 548 | name = Release; 549 | }; 550 | OBJ_50 /* Debug */ = { 551 | isa = XCBuildConfiguration; 552 | buildSettings = { 553 | }; 554 | name = Debug; 555 | }; 556 | OBJ_51 /* Release */ = { 557 | isa = XCBuildConfiguration; 558 | buildSettings = { 559 | }; 560 | name = Release; 561 | }; 562 | /* End XCBuildConfiguration section */ 563 | 564 | /* Begin XCConfigurationList section */ 565 | OBJ_2 /* Build configuration list for PBXProject "WiremockClient" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | OBJ_3 /* Debug */, 569 | OBJ_4 /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Debug; 573 | }; 574 | OBJ_22 /* Build configuration list for PBXNativeTarget "WiremockClientPackageDescription" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | OBJ_23 /* Debug */, 578 | OBJ_24 /* Release */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Debug; 582 | }; 583 | OBJ_28 /* Build configuration list for PBXNativeTarget "WiremockClientTests" */ = { 584 | isa = XCConfigurationList; 585 | buildConfigurations = ( 586 | OBJ_29 /* Debug */, 587 | OBJ_30 /* Release */, 588 | ); 589 | defaultConfigurationIsVisible = 0; 590 | defaultConfigurationName = Debug; 591 | }; 592 | OBJ_37 /* Build configuration list for PBXNativeTarget "WiremockClient" */ = { 593 | isa = XCConfigurationList; 594 | buildConfigurations = ( 595 | OBJ_38 /* Debug */, 596 | OBJ_39 /* Release */, 597 | ); 598 | defaultConfigurationIsVisible = 0; 599 | defaultConfigurationName = Debug; 600 | }; 601 | OBJ_49 /* Build configuration list for PBXAggregateTarget "WiremockClientPackageTests" */ = { 602 | isa = XCConfigurationList; 603 | buildConfigurations = ( 604 | OBJ_50 /* Debug */, 605 | OBJ_51 /* Release */, 606 | ); 607 | defaultConfigurationIsVisible = 0; 608 | defaultConfigurationName = Debug; 609 | }; 610 | /* End XCConfigurationList section */ 611 | }; 612 | rootObject = OBJ_1 /* Project object */; 613 | } 614 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/xcshareddata/xcschemes/WiremockClient.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 32 | 33 | 39 | 40 | 41 | 42 | 44 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/xcshareddata/xcschemes/WiremockClientTests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 16 | 17 | 23 | 24 | 25 | 26 | 28 | 34 | 35 | 36 | 37 | 38 | 48 | 49 | 55 | 56 | 58 | 59 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /WiremockClient.xcodeproj/xcshareddata/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SchemeUserState 5 | 6 | WiremockClient-Package.xcscheme 7 | 8 | 9 | SuppressBuildableAutocreation 10 | 11 | 12 | 13 | --------------------------------------------------------------------------------