diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..3bff981 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,86 @@ +name: Tests + +on: + push: + branches: [ main ] + paths-ignore: [ README.md ] + pull_request: + branches: [ main ] + paths-ignore: [ README.md ] + workflow_dispatch: + +jobs: + formatlint: + name: Format linting + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + - name: Pull formatting docker image + run: docker pull ghcr.io/nicklockwood/swiftformat:latest + - name: Run format linting + run: docker run --rm -v ${{ github.workspace }}:/repo ghcr.io/nicklockwood/swiftformat:latest /repo --lint + + macos: + name: Test on macOS + runs-on: macos-latest + steps: + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: latest-stable + - uses: actions/checkout@v3 + - name: Build and test + run: swift test --parallel --enable-test-discovery + + linux: + name: Test on Linux + runs-on: ubuntu-latest + steps: + - uses: swift-actions/setup-swift@v2 + - uses: actions/checkout@v3 + - name: Test + run: swift test --parallel --enable-code-coverage + - name: Get test coverage html + run: | + llvm-cov show \ + $(swift build --show-bin-path)/GraphQLRxSwiftPackageTests.xctest \ + --instr-profile $(swift build --show-bin-path)/codecov/default.profdata \ + --ignore-filename-regex="\.build|Tests" \ + --format html \ + --output-dir=.test-coverage + - name: Upload test coverage html + uses: actions/upload-artifact@v3 + with: + name: test-coverage-report + path: .test-coverage + + backcompat-ubuntu-22_04: + name: Test Swift ${{ matrix.swift }} on Ubuntu 22.04 + runs-on: ubuntu-22.04 + strategy: + matrix: + swift: ["5.7", "5.8", "5.9", "5.10", "6.0"] + steps: + - uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ matrix.swift }} + - uses: actions/checkout@v3 + - name: Test + run: swift test --parallel + + # Swift versions older than 5.7 don't have builds for 22.04. https://www.swift.org/download/ + backcompat-ubuntu-20_04: + name: Test Swift ${{ matrix.swift }} on Ubuntu 20.04 + runs-on: ubuntu-20.04 + strategy: + matrix: + swift: ["5.4", "5.5", "5.6"] + steps: + - uses: swift-actions/setup-swift@v2 + with: + swift-version: ${{ matrix.swift }} + - uses: actions/checkout@v3 + - name: Test + run: swift test --parallel diff --git a/.gitignore b/.gitignore index 19e5288..5ad9401 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ ### SwiftPM ### .build/ .swiftpm/ + +# VS Code +.vscode diff --git a/Package.resolved b/Package.resolved index e7b6132..7fe0d5b 100644 --- a/Package.resolved +++ b/Package.resolved @@ -6,8 +6,8 @@ "repositoryURL": "https://github.com/GraphQLSwift/Graphiti.git", "state": { "branch": null, - "revision": "c9bc9d1cc9e62e71a824dc178630bfa8b8a6e2a4", - "version": "1.0.0" + "revision": "ed06f0608a72176fd572763660e7492bfb53a419", + "version": "1.15.1" } }, { @@ -15,8 +15,8 @@ "repositoryURL": "https://github.com/GraphQLSwift/GraphQL.git", "state": { "branch": null, - "revision": "e5de315125f8220334ba3799bbd78c7c1ed529f7", - "version": "2.0.0" + "revision": "87649dbc3cdab0be0256c86235f2aec22ec1bfc1", + "version": "2.10.0" } }, { @@ -24,8 +24,17 @@ "repositoryURL": "https://github.com/ReactiveX/RxSwift.git", "state": { "branch": null, - "revision": "7c17a6ccca06b5c107cfa4284e634562ddaf5951", - "version": "6.2.0" + "revision": "b06a8c8596e4c3e8e7788e08e720e3248563ce6a", + "version": "6.7.1" + } + }, + { + "package": "swift-atomics", + "repositoryURL": "https://github.com/apple/swift-atomics.git", + "state": { + "branch": null, + "revision": "cd142fd2f64be2100422d658e7411e39489da985", + "version": "1.2.0" } }, { @@ -33,8 +42,8 @@ "repositoryURL": "https://github.com/apple/swift-collections", "state": { "branch": null, - "revision": "d45e63421d3dff834949ac69d3c37691e994bd69", - "version": "0.0.3" + "revision": "9bf03ff58ce34478e66aaee630e491823326fd06", + "version": "1.1.3" } }, { @@ -42,8 +51,17 @@ "repositoryURL": "https://github.com/apple/swift-nio.git", "state": { "branch": null, - "revision": "d161bf658780b209c185994528e7e24376cf7283", - "version": "2.29.0" + "revision": "9746cf80e29edfef2a39924a66731249223f42a3", + "version": "2.72.0" + } + }, + { + "package": "swift-system", + "repositoryURL": "https://github.com/apple/swift-system.git", + "state": { + "branch": null, + "revision": "d2ba781702a1d8285419c15ee62fd734a9437ff5", + "version": "1.3.2" } } ] diff --git a/Package.swift b/Package.swift index 1154d0a..1866484 100644 --- a/Package.swift +++ b/Package.swift @@ -9,10 +9,10 @@ let package = Package( dependencies: [ .package(url: "https://github.com/GraphQLSwift/GraphQL.git", from: "2.0.0"), .package(url: "https://github.com/GraphQLSwift/Graphiti.git", from: "1.0.0"), - .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.1.0") + .package(url: "https://github.com/ReactiveX/RxSwift.git", from: "6.1.0"), ], targets: [ .target(name: "GraphQLRxSwift", dependencies: ["GraphQL", "Graphiti", "RxSwift"]), - .testTarget(name: "GraphQLRxSwiftTests",dependencies: ["GraphQLRxSwift"]), + .testTarget(name: "GraphQLRxSwiftTests", dependencies: ["GraphQLRxSwift"]), ] ) diff --git a/Sources/GraphQLRxSwift/ObservableEventStream.swift b/Sources/GraphQLRxSwift/ObservableEventStream.swift index 3860015..3ef3406 100644 --- a/Sources/GraphQLRxSwift/ObservableEventStream.swift +++ b/Sources/GraphQLRxSwift/ObservableEventStream.swift @@ -1,22 +1,25 @@ import GraphQL +import NIO import RxSwift // EventStream wrapper for Observable -public class ObservableEventStream : EventStream { +public class ObservableEventStream: EventStream { public var observable: Observable init(_ observable: Observable) { self.observable = observable } + override open func map(_ closure: @escaping (Element) throws -> To) -> EventStream { return ObservableEventStream(observable.map(closure)) } } + // Convenience types public typealias ObservableSubscriptionEventStream = ObservableEventStream> -extension Observable { +public extension Observable { // Convenience method for wrapping Observables in EventStreams - public func toEventStream() -> ObservableEventStream { + func toEventStream() -> ObservableEventStream { return ObservableEventStream(self) } } diff --git a/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionSchema.swift b/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionSchema.swift index e383199..59dbbab 100644 --- a/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionSchema.swift +++ b/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionSchema.swift @@ -4,14 +4,15 @@ import NIO import RxSwift // MARK: Types -struct Email : Encodable { - let from:String - let subject:String - let message:String - let unread:Bool - let priority:Int - - init(from:String, subject:String, message:String, unread:Bool, priority:Int = 0) { + +struct Email: Encodable { + let from: String + let subject: String + let message: String + let unread: Bool + let priority: Int + + init(from: String, subject: String, message: String, unread: Bool, priority: Int = 0) { self.from = from self.subject = subject self.message = message @@ -20,16 +21,17 @@ struct Email : Encodable { } } -struct Inbox : Encodable { - let emails:[Email] +struct Inbox: Encodable { + let emails: [Email] } -struct EmailEvent : Encodable { - let email:Email - let inbox:Inbox +struct EmailEvent: Encodable { + let email: Email + let inbox: Inbox } // MARK: Schema + let EmailType = try! GraphQLObjectType( name: "Email", fields: [ @@ -62,7 +64,7 @@ let InboxType = try! GraphQLObjectType( "unread": GraphQLField( type: GraphQLInt, resolve: { inbox, _, _, _ in - (inbox as! Inbox).emails.filter({$0.unread}).count + (inbox as! Inbox).emails.filter { $0.unread }.count } ), ] @@ -75,7 +77,7 @@ let EmailEventType = try! GraphQLObjectType( ), "inbox": GraphQLField( type: InboxType - ) + ), ] ) let EmailQueryType = try! GraphQLObjectType( @@ -83,7 +85,7 @@ let EmailQueryType = try! GraphQLObjectType( fields: [ "inbox": GraphQLField( type: InboxType - ) + ), ] ) @@ -95,7 +97,7 @@ class EmailDb { var emails: [Email] let publisher: PublishSubject let disposeBag: DisposeBag - + init() { emails = [ Email( @@ -103,32 +105,32 @@ class EmailDb { subject: "Hello", message: "Hello World", unread: false - ) + ), ] publisher = PublishSubject() disposeBag = DisposeBag() } - + /// Adds a new email to the database and triggers all observers - func trigger(email:Email) { + func trigger(email: Email) { emails.append(email) publisher.onNext(email) } - + /// Returns the default email schema, with standard resolvers. func defaultSchema() -> GraphQLSchema { return emailSchemaWithResolvers( - resolve: {emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + resolve: { emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in if let email = emailAny as? Email { return eventLoopGroup.next().makeSucceededFuture(EmailEvent( email: email, inbox: Inbox(emails: self.emails) )) } else { - throw GraphQLError(message: "\(type(of:emailAny)) is not Email") + throw GraphQLError(message: "\(type(of: emailAny)) is not Email") } }, - subscribe: {_, args, _, eventLoopGroup, _ throws -> EventLoopFuture in + subscribe: { _, args, _, eventLoopGroup, _ throws -> EventLoopFuture in let priority = args["priority"].int ?? 0 let filtered = self.publisher.filter { emailAny throws in if let email = emailAny as? Email { @@ -141,10 +143,10 @@ class EmailDb { } ) } - + /// Generates a subscription to the database using the default schema and resolvers - func subscription ( - query:String, + func subscription( + query: String, variableValues: [String: Map] = [:] ) throws -> SubscriptionEventStream { return try createSubscription(schema: defaultSchema(), query: query, variableValues: variableValues) @@ -163,11 +165,11 @@ func emailSchemaWithResolvers(resolve: GraphQLFieldResolve? = nil, subscribe: Gr args: [ "priority": GraphQLArgument( type: GraphQLInt - ) + ), ], resolve: resolve, subscribe: subscribe - ) + ), ] ) ) @@ -186,13 +188,13 @@ func createSubscription( instrumentation: NoOpInstrumentation, schema: schema, request: query, - rootValue: Void(), - context: Void(), + rootValue: (), + context: (), eventLoopGroup: eventLoopGroup, variableValues: variableValues, operationName: nil ).wait() - + if let stream = result.stream { return stream } else { diff --git a/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionTests.swift b/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionTests.swift index ec1677c..9326a16 100644 --- a/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionTests.swift +++ b/Tests/GraphQLRxSwiftTests/GraphQL/SubscriptionTests.swift @@ -1,14 +1,13 @@ import GraphQL +@testable import GraphQLRxSwift import NIO import RxSwift import XCTest -@testable import GraphQLRxSwift /// This follows the graphql-js testing, with deviations where noted. -class SubscriptionTests : XCTestCase { - +class SubscriptionTests: XCTestCase { // MARK: Test primary graphqlSubscribe function - + /// This test is not present in graphql-js, but just tests basic functionality. func testGraphqlSubscribe() throws { let db = EmailDb() @@ -27,7 +26,7 @@ class SubscriptionTests : XCTestCase { } } """ - + let subscriptionResult = try graphqlSubscribe( schema: schema, request: query, @@ -41,33 +40,33 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() }.disposed(by: db.disposeBag) - + db.trigger(email: Email( from: "yuzhi@graphql.org", subject: "Alright", message: "Tests are good", unread: true )) - + XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] )) } - + // MARK: Subscription Initialization Phase /// accepts multiple subscription fields defined in schema @@ -83,17 +82,17 @@ class SubscriptionTests : XCTestCase { args: [ "priority": GraphQLArgument( type: GraphQLInt - ) + ), ], - resolve: {emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + resolve: { emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in let email = emailAny as! Email return eventLoopGroup.next().makeSucceededFuture(EmailEvent( email: email, inbox: Inbox(emails: db.emails) )) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) } ), "notImportantEmail": GraphQLField( @@ -101,19 +100,19 @@ class SubscriptionTests : XCTestCase { args: [ "priority": GraphQLArgument( type: GraphQLInt - ) + ), ], - resolve: {emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + resolve: { emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in let email = emailAny as! Email return eventLoopGroup.next().makeSucceededFuture(EmailEvent( email: email, inbox: Inbox(emails: db.emails) )) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) } - ) + ), ] ) ) @@ -135,29 +134,29 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() }.disposed(by: db.disposeBag) - + db.trigger(email: Email( from: "yuzhi@graphql.org", subject: "Alright", message: "Tests are good", unread: true )) - + XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] )) } @@ -178,24 +177,24 @@ class SubscriptionTests : XCTestCase { fields: [ "importantEmail": GraphQLField( type: EmailEventType, - resolve: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(nil) + resolve: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(nil) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in didResolveImportantEmail = true return eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) } ), "notImportantEmail": GraphQLField( type: EmailEventType, - resolve: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(nil) + resolve: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(nil) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in didResolveNonImportantEmail = true return eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) } - ) + ), ] ) ) @@ -217,8 +216,8 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - - let _ = stream.observable.subscribe{ event in + + let _ = stream.observable.subscribe { event in let _ = try! event.element!.wait() }.disposed(by: db.disposeBag) db.trigger(email: Email( @@ -267,11 +266,11 @@ class SubscriptionTests : XCTestCase { /// 'throws an error if subscribe does not return an iterator' func testErrorIfSubscribeIsntIterator() throws { let schema = emailSchemaWithResolvers( - resolve: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(nil) + resolve: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(nil) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture("test") + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture("test") } ) XCTAssertThrowsError( @@ -314,27 +313,26 @@ class SubscriptionTests : XCTestCase { // Throwing an error verifyError(schema: emailSchemaWithResolvers( - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + subscribe: { _, _, _, _, _ throws -> EventLoopFuture in throw GraphQLError(message: "test error") } )) // Resolving to an error verifyError(schema: emailSchemaWithResolvers( - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(GraphQLError(message: "test error")) + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(GraphQLError(message: "test error")) } )) // Rejecting with an error verifyError(schema: emailSchemaWithResolvers( - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeFailedFuture(GraphQLError(message: "test error")) + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeFailedFuture(GraphQLError(message: "test error")) } )) } - /// 'resolves to an error for source event stream resolver errors' // Tests above cover this @@ -360,19 +358,12 @@ class SubscriptionTests : XCTestCase { try db.subscription( query: query, variableValues: [ - "priority": "meow" + "priority": "meow", ] ) - ) { error in - let graphQLError = error as! GraphQLError - XCTAssertEqual( - graphQLError.message, - "Variable \"$priority\" got invalid value \"meow\".\nExpected type \"Int\", found \"meow\"." - ) - } + ) } - // MARK: Subscription Publish Phase /// 'produces a payload for a single subscriber' @@ -396,12 +387,12 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() }.disposed(by: db.disposeBag) - + db.trigger(email: Email( from: "yuzhi@graphql.org", subject: "Alright", @@ -410,14 +401,14 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] )) } @@ -443,13 +434,13 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + // Subscription 1 var sub1Value = GraphQLResult() let _ = stream.observable.subscribe { event in sub1Value = try! event.element!.wait() }.disposed(by: db.disposeBag) - + // Subscription 2 var sub2Value = GraphQLResult() let _ = stream.observable.subscribe { event in @@ -462,24 +453,24 @@ class SubscriptionTests : XCTestCase { message: "Tests are good", unread: true )) - + let expected = GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] ) - + XCTAssertEqual(sub1Value, expected) XCTAssertEqual(sub2Value, expected) } - + /// 'produces a payload per subscription event' func testPayloadPerEvent() throws { let db = EmailDb() @@ -501,11 +492,10 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() - print(currentResult) }.disposed(by: db.disposeBag) db.trigger(email: Email( @@ -516,17 +506,17 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] )) - + db.trigger(email: Email( from: "hyo@graphql.org", subject: "Tools", @@ -535,18 +525,18 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "hyo@graphql.org", - "subject": "Tools" + "subject": "Tools", ], - "inbox":[ + "inbox": [ "unread": 2, - "total": 3 - ] + "total": 3, + ], ]] )) } - + /// Tests that subscriptions use arguments correctly. /// This is not in the graphql-js tests. func testArguments() throws { @@ -569,7 +559,7 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() @@ -584,18 +574,18 @@ class SubscriptionTests : XCTestCase { )) let firstMessageExpected = GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 - ] + "total": 2, + ], ]] ) XCTAssertEqual(currentResult, firstMessageExpected) - + // Low priority email shouldn't trigger an event db.trigger(email: Email( from: "hyo@graphql.org", @@ -605,7 +595,7 @@ class SubscriptionTests : XCTestCase { priority: 2 )) XCTAssertEqual(currentResult, firstMessageExpected) - + // Higher priority one should trigger again db.trigger(email: Email( from: "hyo@graphql.org", @@ -616,14 +606,14 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "hyo@graphql.org", - "subject": "Tools" + "subject": "Tools", ], - "inbox":[ + "inbox": [ "unread": 3, - "total": 4 - ] + "total": 4, + ], ]] )) } @@ -649,25 +639,25 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let subscriber = stream.observable.subscribe { event in currentResult = try! event.element!.wait() } - + let expected = GraphQLResult( data: ["importantEmail": [ - "email":[ + "email": [ "from": "yuzhi@graphql.org", - "subject": "Alright" + "subject": "Alright", ], - "inbox":[ + "inbox": [ "unread": 1, - "total": 2 + "total": 2, ], ]] ) - + db.trigger(email: Email( from: "yuzhi@graphql.org", subject: "Alright", @@ -699,18 +689,18 @@ class SubscriptionTests : XCTestCase { let db = EmailDb() let schema = emailSchemaWithResolvers( - resolve: {emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + resolve: { emailAny, _, _, eventLoopGroup, _ throws -> EventLoopFuture in let email = emailAny as! Email if email.subject == "Goodbye" { // Force the system to fail here. - throw GraphQLError(message:"Never leave.") + throw GraphQLError(message: "Never leave.") } return eventLoopGroup.next().makeSucceededFuture(EmailEvent( email: email, inbox: Inbox(emails: db.emails) )) }, - subscribe: {_, _, _, eventLoopGroup, _ throws -> EventLoopFuture in - return eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) + subscribe: { _, _, _, eventLoopGroup, _ throws -> EventLoopFuture in + eventLoopGroup.next().makeSucceededFuture(db.publisher.toEventStream()) } ) @@ -727,12 +717,12 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() }.disposed(by: db.disposeBag) - + db.trigger(email: Email( from: "yuzhi@graphql.org", subject: "Hello", @@ -741,9 +731,9 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ - "subject": "Hello" - ] + "email": [ + "subject": "Hello", + ], ]] )) @@ -757,7 +747,7 @@ class SubscriptionTests : XCTestCase { XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": nil], errors: [ - GraphQLError(message: "Never leave.") + GraphQLError(message: "Never leave."), ] )) @@ -770,19 +760,19 @@ class SubscriptionTests : XCTestCase { )) XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": [ - "email":[ - "subject": "Bonjour" - ] + "email": [ + "subject": "Bonjour", + ], ]] )) } - + /// 'should pass through error thrown in source event stream' // Not necessary - Pub/sub implementation handles event erroring - + /// 'should resolve GraphQL error from source event stream' // Not necessary - Pub/sub implementation handles event erroring - + /// Test incorrect observable publish type errors func testErrorWrongObservableType() throws { let db = EmailDb() @@ -804,18 +794,18 @@ class SubscriptionTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in currentResult = try! event.element!.wait() }.disposed(by: db.disposeBag) - + db.publisher.onNext("String instead of email") - + XCTAssertEqual(currentResult, GraphQLResult( data: ["importantEmail": nil], errors: [ - GraphQLError(message: "String is not Email") + GraphQLError(message: "String is not Email"), ] )) } diff --git a/Tests/GraphQLRxSwiftTests/Graphiti/GraphitiRxSwiftTests.swift b/Tests/GraphQLRxSwiftTests/Graphiti/GraphitiRxSwiftTests.swift index b4508dc..2f1bbcc 100644 --- a/Tests/GraphQLRxSwiftTests/Graphiti/GraphitiRxSwiftTests.swift +++ b/Tests/GraphQLRxSwiftTests/Graphiti/GraphitiRxSwiftTests.swift @@ -1,55 +1,55 @@ -import XCTest -import GraphQL import Graphiti +import GraphQL +@testable import GraphQLRxSwift import NIO import RxSwift -@testable import GraphQLRxSwift +import XCTest let pubsub = PublishSubject() -struct ID : Codable { +struct ID: Codable { let id: String - + init(_ id: String) { self.id = id } - + init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() - self.id = try container.decode(String.self) + id = try container.decode(String.self) } - + func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() - try container.encode(self.id) + try container.encode(id) } } -struct User : Codable { +struct User: Codable { let id: String let name: String? - + init(id: String, name: String?) { self.id = id self.name = name } - + init(_ input: UserInput) { - self.id = input.id - self.name = input.name + id = input.id + name = input.name } - - func toEvent(context: HelloContext, arguments: NoArguments) throws -> UserEvent { + + func toEvent(context _: HelloContext, arguments _: NoArguments) throws -> UserEvent { return UserEvent(user: self) } } -struct UserInput : Codable { +struct UserInput: Codable { let id: String let name: String? } -struct UserEvent : Codable { +struct UserEvent: Codable { let user: User } @@ -60,62 +60,62 @@ final class HelloContext { } struct HelloResolver { - func hello(context: HelloContext, arguments: NoArguments) -> String { + func hello(context: HelloContext, arguments _: NoArguments) -> String { context.hello() } - + func asyncHello( context: HelloContext, - arguments: NoArguments, + arguments _: NoArguments, group: EventLoopGroup ) -> EventLoopFuture { group.next().makeSucceededFuture(context.hello()) } - - struct FloatArguments : Codable { + + struct FloatArguments: Codable { let float: Float } - - func getFloat(context: HelloContext, arguments: FloatArguments) -> Float { + + func getFloat(context _: HelloContext, arguments: FloatArguments) -> Float { arguments.float } - - struct IDArguments : Codable { + + struct IDArguments: Codable { let id: ID } - - func getId(context: HelloContext, arguments: IDArguments) -> ID { + + func getId(context _: HelloContext, arguments: IDArguments) -> ID { arguments.id } - - func getUser(context: HelloContext, arguments: NoArguments) -> User { + + func getUser(context _: HelloContext, arguments _: NoArguments) -> User { User(id: "123", name: "John Doe") } - - struct AddUserArguments : Codable { + + struct AddUserArguments: Codable { let user: UserInput } - - func addUser(context: HelloContext, arguments: AddUserArguments) -> User { + + func addUser(context _: HelloContext, arguments: AddUserArguments) -> User { User(arguments.user) } - - func subscribeUser(context: HelloContext, arguments: NoArguments) -> EventStream { + + func subscribeUser(context _: HelloContext, arguments _: NoArguments) -> EventStream { pubsub.toEventStream() } } -struct HelloAPI : API { +struct HelloAPI: API { let resolver = HelloResolver() let context = HelloContext() - + let schema = try! Schema { Scalar(Float.self) .description("The `Float` scalar type represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).") Scalar(ID.self) .description("The `ID` scalar type represents a unique identifier.") - + Type(User.self) { Field("id", at: \.id) Field("name", at: \.name) @@ -125,23 +125,23 @@ struct HelloAPI : API { InputField("id", at: \.id) InputField("name", at: \.name) } - + Type(UserEvent.self) { Field("user", at: \.user) } - + Query { Field("hello", at: HelloResolver.hello) Field("asyncHello", at: HelloResolver.asyncHello) - + Field("float", at: HelloResolver.getFloat) { Argument("float", at: \.float) } - + Field("id", at: HelloResolver.getId) { Argument("id", at: \.id) } - + Field("user", at: HelloResolver.getUser) } @@ -150,7 +150,7 @@ struct HelloAPI : API { Argument("user", at: \.user) } } - + Subscription { SubscriptionField("subscribeUser", as: User.self, atSub: HelloResolver.subscribeUser) SubscriptionField("subscribeUserEvent", at: User.toEvent, atSub: HelloResolver.subscribeUser) @@ -158,18 +158,18 @@ struct HelloAPI : API { } } -class HelloWorldTests : XCTestCase { +class HelloWorldTests: XCTestCase { private let api = HelloAPI() private var group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) - + deinit { try? self.group.syncShutdownGracefully() } - + /// Tests subscription when the sourceEventStream type matches the resolved type (i.e. the normal resolution function should just short-circuit to the sourceEventStream object) func testSubscriptionSelf() throws { let disposeBag = DisposeBag() - + let request = """ subscription { subscribeUser { @@ -178,7 +178,7 @@ class HelloWorldTests : XCTestCase { } } """ - + let subscriptionResult = try api.subscribe( request: request, context: api.context, @@ -192,9 +192,9 @@ class HelloWorldTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + let expectation = XCTestExpectation() - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in let resultFuture = event.element! @@ -202,7 +202,7 @@ class HelloWorldTests : XCTestCase { currentResult = result expectation.fulfill() } - resultFuture.whenFailure { error in + resultFuture.whenFailure { _ in XCTFail() } }.disposed(by: disposeBag) @@ -214,15 +214,15 @@ class HelloWorldTests : XCTestCase { XCTAssertEqual(currentResult, GraphQLResult(data: [ "subscribeUser": [ "id": "124", - "name": "Jerry" - ] + "name": "Jerry", + ], ])) } - + /// Tests subscription when the sourceEventStream type does not match the resolved type (i.e. there is a non-trivial resolution function that transforms the sourceEventStream object) func testSubscriptionEvent() throws { let disposeBag = DisposeBag() - + let request = """ subscription { subscribeUserEvent { @@ -233,7 +233,7 @@ class HelloWorldTests : XCTestCase { } } """ - + let subscriptionResult = try api.subscribe( request: request, context: api.context, @@ -247,9 +247,9 @@ class HelloWorldTests : XCTestCase { XCTFail("stream isn't ObservableSubscriptionEventStream") return } - + let expectation = XCTestExpectation() - + var currentResult = GraphQLResult() let _ = stream.observable.subscribe { event in let resultFuture = event.element! @@ -257,7 +257,7 @@ class HelloWorldTests : XCTestCase { currentResult = result expectation.fulfill() } - resultFuture.whenFailure { error in + resultFuture.whenFailure { _ in XCTFail() } }.disposed(by: disposeBag) @@ -270,9 +270,9 @@ class HelloWorldTests : XCTestCase { "subscribeUserEvent": [ "user": [ "id": "124", - "name": "Jerry" - ] - ] + "name": "Jerry", + ], + ], ])) } }