Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/Model/BodyBuilder+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public extension BodyBuilder {

/// Add a null body with the specified `contentType` (defaults to `text/plain`).
@discardableResult
func body(contentType: String? = "text/plain") throws -> Self {
func body(contentType: String = "text/plain") throws -> Self {
try body(nil, contentType: contentType)
}

Expand Down
5 changes: 0 additions & 5 deletions Sources/Model/HeaderBuilder+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ public extension HeaderBuilder {
try header("Content-Type", value: contentType)
}

@discardableResult
func header(_ name: String, value: String) throws -> Self {
try header(name, values: [value])
}

@discardableResult
func header(_ name: String, matching: AnyMatcher) throws -> Self {
let valueString = try String(data: JSONEncoder().encode(matching), encoding: .utf8)!
Expand Down
224 changes: 102 additions & 122 deletions Sources/Model/ProviderVerifier+Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
//

import Foundation
import PactSwiftMockServer

public extension ProviderVerifier {

Expand Down Expand Up @@ -117,145 +118,124 @@

extension ProviderVerifier.Options {

/// Newline delimited provider verification arguments
var args: String {
// Verification arguments to pass to pactffi_verify()
var newLineDelimitedArgs = [String]()
/// The typed options passed to `PactSwiftMockServer`'s handle-based verifier.
///
/// - Note: `logLevel` is currently not mapped. The `pactffi_verifier_*` API has no
/// per-verification log level setter (verification logging is configured globally).
var verificationOptions: VerificationOptions {

Check failure on line 125 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
VerificationOptions(
provider: providerInfo,
sources: sources,
filter: filter,
consumerFilters: consumerFilters,
stateChange: stateChange,
publish: publish
)
}

// Set verified provider port
newLineDelimitedArgs.append("--port\n\(self.port)")
private var providerInfo: VerificationOptions.Provider {

Check failure on line 136 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
let path = (providerURL?.path).flatMap { $0.isEmpty ? nil : $0 } ?? "/"
return VerificationOptions.Provider(
name: providerName,
scheme: providerURL?.scheme ?? "http",
host: providerURL?.host ?? "localhost",
port: UInt16(port),
path: path
)
}

// Set verified provider url
if let providerURL = providerURL {
newLineDelimitedArgs.append("--hostname\n\(providerURL.absoluteString)")
/// The provider name only exists on a broker source; otherwise the FFI default is used.
private var providerName: String {
if case .broker(let broker) = pactsSource {
return broker.providerName
}
return "provider"
}

// Pacts source
private var sources: [VerificationOptions.Source] {

Check failure on line 155 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
switch pactsSource {

case .broker(let broker):
// Set broker url
newLineDelimitedArgs.append("--broker-url\n\(broker.url)")

// Broker authentication type
// Authenticate with username and password
switch broker.authentication {
case .auth(let auth):
newLineDelimitedArgs.append("--user\n\(auth.username)")
newLineDelimitedArgs.append("--password\n\(auth.password)")

// Authenticate with a Token (Pactflow)
case .token(let auth):
newLineDelimitedArgs.append("--token\n\(auth.token)")
}

// Use the pact for provider with name
newLineDelimitedArgs.append("--provider-name\n\(broker.providerName)")

// Publishing verification results back to Broker
if broker.publishVerificationResult, let providerVersion = broker.providerVersion, providerVersion.isEmpty == false {
newLineDelimitedArgs.append("--publish")
newLineDelimitedArgs.append("--provider-version\n\(providerVersion)")

if let providerTags = broker.providerTags, providerTags.isEmpty == false {
newLineDelimitedArgs.append("--provider-tags\n\(providerTags.joined(separator: ","))")
}
}

// Consumer tags
broker.consumerTags?.forEach {
do {
newLineDelimitedArgs.append("--consumer-version-selectors\n\(try $0.toJSONString())")
} catch {
Logger.log(message: "Failed to convert provider version to JSON representaion: \(String(describing: broker.consumerTags))")
}
}

// Pending pacts
if broker.includePending == true {
newLineDelimitedArgs.append("--enable-pending\ntrue")
}

// WIP pacts
if let includeWIP = broker.includeWIP {
// Enable pending pacts only if it wasn not set already!
let enablePendingArgs = "--enable-pending\ntrue"
if newLineDelimitedArgs.contains(where: { $0 == enablePendingArgs }) == false {
newLineDelimitedArgs.append(enablePendingArgs)
}

// Set the date from which to include WIP pacts
newLineDelimitedArgs.append("--include-wip-pacts-since\n\(includeWIP.sinceDate.iso8601short)")

// Explicitly set provider version argument but only when not publishing verification result (otherwise it would be duplicated)
// See [Work In Progress - Technical details](https://docs.pact.io/pact_broker/advanced_topics/wip_pacts/#technical-details) for more.
if broker.publishVerificationResult == false {
newLineDelimitedArgs.append("--provider-version\n\(includeWIP.providerVersion)")
}
}

// Verify pacts from directories
case .directories(let pactDirs) where pactDirs.isEmpty == false:
pactDirs.forEach { newLineDelimitedArgs.append("--dir\n\($0)") }

// Verify specific pact files
case .files(let files) where files.isEmpty == false:
files.forEach { newLineDelimitedArgs.append("--file\n\($0)") }

// Verify pacts from specific URLs
case .urls(let pactURLs) where pactURLs.isEmpty == false:
pactURLs.forEach { newLineDelimitedArgs.append("--url\n\($0)") }

default:
break
return [.broker(broker.brokerSource)]
case .directories(let directories):
return directories.map { .directory($0) }
case .files(let files):
return files.map { .file($0) }
case .urls(let urls):
return urls.map { .url($0, authentication: nil) }
}
}

// Set state filters
if let filterProviderStates = filterPacts {
switch filterProviderStates {

// Only test interactions with no specific state defined
case .noState:
newLineDelimitedArgs.append("--filter-no-state\ntrue")

// Only test interactions with specific states
case .states(let states) where states.isEmpty == false:
states.forEach { newLineDelimitedArgs.append("--filter-state\n\($0)") }

// Only test interactions with specific descriptions
case .descriptions(let descriptions) where descriptions.isEmpty == false:
descriptions.forEach { newLineDelimitedArgs.append("--filter-description\n\($0)") }

// Only test pact contracts with specific consumers
case .consumers(let consumers) where consumers.isEmpty == false:
consumers.forEach { newLineDelimitedArgs.append("--filter-consumer\n\($0)") }

default:
break
}
/// - Note: `pactffi_verifier_set_filter_info` accepts a single state and a single
/// description, so only the first value is forwarded when several are provided.
private var filter: VerificationOptions.Filter? {

Check failure on line 170 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
switch filterPacts {
case .noState:
return VerificationOptions.Filter(noState: true)
case .states(let states):
return VerificationOptions.Filter(state: states.first)
case .descriptions(let descriptions):
return VerificationOptions.Filter(description: descriptions.first)
case .consumers, .none:
return nil
}
}

// State change URL
if let stateChangeURL = stateChangeURL {
newLineDelimitedArgs.append("--state-change-url\n\(stateChangeURL.absoluteString)")
private var consumerFilters: [String] {
if case .consumers(let consumers) = filterPacts {
return consumers
}
return []
}

// Set logging level
newLineDelimitedArgs.append("--loglevel\n\(self.logLevel.rawValue)")

// Convert all verification arguments to a `String` and return it
return newLineDelimitedArgs.joined(separator: "\n")
private var stateChange: VerificationOptions.StateChange? {

Check failure on line 190 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
stateChangeURL.map { VerificationOptions.StateChange(url: $0) }
}

private var publish: VerificationOptions.Publish? {

Check failure on line 194 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
guard
case .broker(let broker) = pactsSource,
broker.publishVerificationResult,
let providerVersion = broker.providerVersion, providerVersion.isEmpty == false
else {
return nil
}
return VerificationOptions.Publish(
providerVersion: providerVersion,
providerTags: broker.providerTags ?? []
)
}
}

private extension Date {
private extension PactBroker {

var brokerSource: VerificationOptions.Broker {

Check failure on line 211 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
VerificationOptions.Broker(
url: URL(string: url) ?? URL(fileURLWithPath: url),
authentication: brokerAuthentication,
enablePending: includePending ?? (includeWIP != nil),
includeWIPPactsSince: includeWIP?.sinceDate,
providerTags: providerTags ?? [],
consumerVersionSelectors: consumerVersionSelectorStrings
)
}

/// Date represented as string in short ISO8601 format (eg: "2021-08-24")
var iso8601short: String {
let formatter = DateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
return formatter.string(from: self)
var brokerAuthentication: VerificationOptions.Authentication {

Check failure on line 222 in Sources/Model/ProviderVerifier+Options.swift

View workflow job for this annotation

GitHub Actions / 🤖 Test iOS (macos-14, macos)

cannot find type 'VerificationOptions' in scope
switch authentication {
case .auth(let simple):
return .basic(username: simple.username, password: simple.password)
case .token(let apiToken):
return .token(apiToken.token)
}
}

var consumerVersionSelectorStrings: [String] {
(consumerTags ?? []).compactMap { selector in
do {
return try selector.toJSONString()
} catch {
Logger.log(message: "Failed to encode consumer version selector: \(error)")
return nil
}
}
}
}
2 changes: 1 addition & 1 deletion Sources/ProviderVerifier.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public final class ProviderVerifier {
///
@discardableResult
public func verify(options: Options, file: FileString? = #file, line: UInt? = #line, completionBlock: (() -> Void)? = nil) -> Result<Bool, ProviderVerifier.VerificationError> {
switch verifier.verifyProvider(options: options.args) {
switch verifier.verifyProvider(options: options.verificationOptions) {
case .success(let value):
completionBlock?()
return .success(value)
Expand Down
3 changes: 2 additions & 1 deletion Tests/Model/PactTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import XCTest
final class PactTests: XCTestCase {

func testPactVersion() throws {
XCTAssertEqual(Pact.version, "0.4.0")
let pact = Pact(consumer: "Consumer", provider: "Provider")
XCTAssertEqual(pact.ffi_version, "0.5.4")
}
}
Loading
Loading