diff --git a/Package.swift b/Package.swift index e6421c2..c724a58 100644 --- a/Package.swift +++ b/Package.swift @@ -44,6 +44,7 @@ let package = Package( ), .testTarget(name: "WebPushTests", dependencies: [ .product(name: "AsyncHTTPClient", package: "async-http-client"), + .product(name: "Crypto", package: "swift-crypto"), .product(name: "Logging", package: "swift-log"), .product(name: "NIOCore", package: "swift-nio"), .product(name: "ServiceLifecycle", package: "swift-service-lifecycle"), diff --git a/README.md b/README.md index fbf476a..222be0f 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,7 @@ Please check the [releases](https://github.com/mochidev/swift-webpush/releases) dependencies: [ .package( url: "https://github.com/mochidev/swift-webPush.git", - .upToNextMinor(from: "0.1.4") + .upToNextMinor(from: "0.2.0") ), ], ... @@ -37,13 +37,15 @@ targets: [ .target( name: "MyPackage", dependencies: [ - "WebPush", + .product(name: "WebPush", package: "swift-webpush"), + ... ] ), .testTarget( name: "MyPackageTests", dependencies: [ - "WebPushTesting", + .product(name: "WebPushTesting", package: "swift-webpush"), + ... ] ), ] diff --git a/Tests/WebPushTests/VAPIDConfiguration+Testing.swift b/Tests/WebPushTests/VAPIDConfiguration+Testing.swift index be19adf..3426ea4 100644 --- a/Tests/WebPushTests/VAPIDConfiguration+Testing.swift +++ b/Tests/WebPushTests/VAPIDConfiguration+Testing.swift @@ -10,6 +10,7 @@ import Foundation import WebPush extension VAPID.Configuration { + /// Make a new configuration useful for testing against. static func makeTesting() -> VAPID.Configuration { VAPID.Configuration( key: VAPID.Key(), diff --git a/Tests/WebPushTests/VAPIDConfigurationTests.swift b/Tests/WebPushTests/VAPIDConfigurationTests.swift index 0244c2d..32342b8 100644 --- a/Tests/WebPushTests/VAPIDConfigurationTests.swift +++ b/Tests/WebPushTests/VAPIDConfigurationTests.swift @@ -260,6 +260,54 @@ struct VAPIDConfigurationTests { """ ) } + + @Test func decodesIncompleteConfiguration() throws { + #expect( + try JSONDecoder().decode(VAPID.Configuration.self, from: Data( + """ + { + "contactInformation" : "mailto:test@example.com", + "expirationDuration" : 79200, + "primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=", + "validityDuration" : 72000 + } + """.utf8 + )) == + VAPID.Configuration( + key: key1, + contactInformation: .email("test@example.com") + ) + ) + } + + @Test func decodesWholeConfiguration() throws { + #expect( + try JSONDecoder().decode(VAPID.Configuration.self, from: Data( + """ + { + "contactInformation" : "mailto:test@example.com", + "deprecatedKeys" : [ + "bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE=" + ], + "expirationDuration" : 3600, + "keys" : [ + "wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk=" + ], + "primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=", + "validityDuration" : 36000 + } + """.utf8 + )) == + VAPID.Configuration( + primaryKey: key1, + keys: [key2], + deprecatedKeys: [key1, key2, key3], + contactInformation: .email("test@example.com"), + expirationDuration: .hours(1), + validityDuration: .hours(10) + ) + ) + } } @Suite @@ -303,6 +351,11 @@ struct VAPIDConfigurationTests { #expect(VAPID.Configuration.Duration.seconds(175) > VAPID.Configuration.Duration.minutes(2)) } + @Test func addingToDates() { + let now = Date() + #expect(now.adding(.seconds(5)) == now.addingTimeInterval(5)) + } + @Test func coding() throws { #expect(String(decoding: try JSONEncoder().encode(VAPID.Configuration.Duration(60)), as: UTF8.self) == "60") diff --git a/Tests/WebPushTests/WebPushTests.swift b/Tests/WebPushTests/WebPushTests.swift index 9a25d46..89024cb 100644 --- a/Tests/WebPushTests/WebPushTests.swift +++ b/Tests/WebPushTests/WebPushTests.swift @@ -6,6 +6,8 @@ // Copyright © 2024 Mochi Development, Inc. All rights reserved. // +import AsyncHTTPClient +@preconcurrency import Crypto import Foundation import Logging import ServiceLifecycle @@ -14,27 +16,60 @@ import Testing @Suite("WebPush Manager") struct WebPushManagerTests { - @Test func webPushManagerInitializesOnItsOwn() async throws { - let manager = WebPushManager(vapidConfiguration: .makeTesting()) - await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await manager.run() + @Suite + struct Initialization { + @Test func managerInitializesOnItsOwn() async throws { + let manager = WebPushManager(vapidConfiguration: .makeTesting()) + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await manager.run() + } + group.cancelAll() + } + } + + @Test func managerInitializesAsService() async throws { + let logger = Logger(label: "ServiceLogger", factory: { PrintLogHandler(label: $0, metadataProvider: $1) }) + let manager = WebPushManager( + vapidConfiguration: .makeTesting(), + logger: logger + ) + await withThrowingTaskGroup(of: Void.self) { group in + group.addTask { + try await ServiceGroup(services: [manager], logger: logger).run() + } + group.cancelAll() } - group.cancelAll() } } - @Test func webPushManagerInitializesAsService() async throws { - let logger = Logger(label: "ServiceLogger", factory: { PrintLogHandler(label: $0, metadataProvider: $1) }) - let manager = WebPushManager( - vapidConfiguration: .makeTesting(), - logger: logger - ) - await withThrowingTaskGroup(of: Void.self) { group in - group.addTask { - try await ServiceGroup(services: [manager], logger: logger).run() + @Suite("Sending Messages") + struct SendingMessages { + @Test func sendSuccessfulTextMessage() async throws { + try await confirmation { requestWasMade in + let vapidConfiguration = VAPID.Configuration.makeTesting() + + let subscriberPrivateKey = P256.KeyAgreement.PrivateKey(compactRepresentable: false) + var authenticationSecret: [UInt8] = Array(repeating: 0, count: 16) + for index in authenticationSecret.indices { authenticationSecret[index] = .random(in: .min ... .max) } + + let subscriber = Subscriber( + endpoint: URL(string: "https://example.com/subscriber")!, + userAgentKeyMaterial: UserAgentKeyMaterial(publicKey: subscriberPrivateKey.publicKey, authenticationSecret: Data(authenticationSecret)), + vapidKeyID: vapidConfiguration.primaryKey!.id + ) + + let manager = WebPushManager( + vapidConfiguration: vapidConfiguration, + logger: Logger(label: "WebPushManagerTests", factory: { PrintLogHandler(label: $0, metadataProvider: $1) }), + executor: .httpClient(MockHTTPClient({ request in + requestWasMade() + return HTTPClientResponse(status: .created) + })) + ) + + try await manager.send(string: "hello", to: subscriber) } - group.cancelAll() } }