Skip to content

Commit

Permalink
Improved code coverage from 34% to 50%
Browse files Browse the repository at this point in the history
  • Loading branch information
dimitribouniol committed Dec 15, 2024
1 parent a8bcb64 commit 06962c3
Show file tree
Hide file tree
Showing 3 changed files with 356 additions and 11 deletions.
32 changes: 21 additions & 11 deletions Tests/WebPushTests/Base64URLCodingTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,26 @@ import Foundation
import Testing
@testable import WebPush

@Test func base64URLDecoding() async throws {
let string = ">>> Hello, swift-webpush world??? 🎉"
let base64Encoded = "Pj4+IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8/IPCfjok="
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
#expect(String(decoding: Data(base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
#expect(String(decoding: Data(base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
}
@Suite("Base 64 URL Coding")
struct Base64URLCoding {
@Test func base64URLDecoding() async throws {
let string = ">>> Hello, swift-webpush world??? 🎉"
let base64Encoded = "Pj4+IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8/IPCfjok="
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
#expect(String(decoding: Data(base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
#expect(String(decoding: Data(base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
#expect(String(decoding: [UInt8](base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
#expect(String(decoding: [UInt8](base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
}

@Test func invalidBase64URLDecoding() async throws {
#expect(Data(base64URLEncoded: " ") == nil)
}

@Test func base64URLEncoding() async throws {
let string = ">>> Hello, swift-webpush world??? 🎉"
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
#expect(Array(string.utf8).base64URLEncodedString() == base64URLEncoded)
@Test func base64URLEncoding() async throws {
let string = ">>> Hello, swift-webpush world??? 🎉"
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
#expect([UInt8](string.utf8).base64URLEncodedString() == base64URLEncoded)
#expect(Data(string.utf8).base64URLEncodedString() == base64URLEncoded)
}
}
304 changes: 304 additions & 0 deletions Tests/WebPushTests/VAPIDConfigurationTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,304 @@
//
// VAPIDConfigurationTests.swift
// swift-webpush
//
// Created by Dimitri Bouniol on 2024-12-15.
// Copyright © 2024 Mochi Development, Inc. All rights reserved.
//

import Crypto
import Foundation
import Testing
@testable import WebPush

@Suite("VAPID Configuration Tests")
struct VAPIDConfigurationTests {
@Suite
struct Initialization {
let key1 = try! VAPID.Key(base64URLEncoded: "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=")
let key2 = try! VAPID.Key(base64URLEncoded: "wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk=")
let key3 = try! VAPID.Key(base64URLEncoded: "bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE=")

@Test func primaryKeyOnly() {
let config = VAPID.Configuration(
key: key1,
contactInformation: .email("[email protected]")
)
#expect(config.primaryKey == key1)
#expect(config.keys == [key1])
#expect(config.deprecatedKeys == nil)
#expect(config.contactInformation == .email("[email protected]"))
#expect(config.expirationDuration == .hours(22))
#expect(config.validityDuration == .hours(20))
}

@Test func emptyDeprecatedKeys() {
let config = VAPID.Configuration(
key: key1,
deprecatedKeys: [],
contactInformation: .url(URL(string: "https://example.com")!),
expirationDuration: .hours(24),
validityDuration: .hours(12)
)
#expect(config.primaryKey == key1)
#expect(config.keys == [key1])
#expect(config.deprecatedKeys == nil)
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
#expect(config.expirationDuration == .hours(24))
#expect(config.validityDuration == .hours(12))
}

@Test func deprecatedKeys() {
let config = VAPID.Configuration(
key: key1,
deprecatedKeys: [key2, key3],
contactInformation: .email("[email protected]")
)
#expect(config.primaryKey == key1)
#expect(config.keys == [key1])
#expect(config.deprecatedKeys == [key2, key3])
#expect(config.contactInformation == .email("[email protected]"))
#expect(config.expirationDuration == .hours(22))
#expect(config.validityDuration == .hours(20))
}

@Test func deprecatedAndPrimaryKeys() {
let config = VAPID.Configuration(
key: key1,
deprecatedKeys: [key2, key3, key1],
contactInformation: .url(URL(string: "https://example.com")!),
expirationDuration: .hours(24),
validityDuration: .hours(12)
)
#expect(config.primaryKey == key1)
#expect(config.keys == [key1])
#expect(config.deprecatedKeys == [key2, key3])
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
#expect(config.expirationDuration == .hours(24))
#expect(config.validityDuration == .hours(12))
}

@Test func multipleKeys() throws {
let config = try VAPID.Configuration(
primaryKey: nil,
keys: [key1, key2],
deprecatedKeys: nil,
contactInformation: .email("[email protected]")
)
#expect(config.primaryKey == nil)
#expect(config.keys == [key1, key2])
#expect(config.deprecatedKeys == nil)
#expect(config.contactInformation == .email("[email protected]"))
#expect(config.expirationDuration == .hours(22))
#expect(config.validityDuration == .hours(20))
}

@Test func noKeys() throws {
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
try VAPID.Configuration(
primaryKey: nil,
keys: [],
deprecatedKeys: [key2, key3],
contactInformation: .email("[email protected]")
)
}
}

@Test func multipleAndDeprecatedKeys() throws {
let config = try VAPID.Configuration(
primaryKey: nil,
keys: [key1, key2],
deprecatedKeys: [key2],
contactInformation: .email("[email protected]")
)
#expect(config.primaryKey == nil)
#expect(config.keys == [key1, key2])
#expect(config.deprecatedKeys == nil)
#expect(config.contactInformation == .email("[email protected]"))
#expect(config.expirationDuration == .hours(22))
#expect(config.validityDuration == .hours(20))
}

@Test func multipleAndPrimaryKeys() throws {
let config = try VAPID.Configuration(
primaryKey: key1,
keys: [key2],
deprecatedKeys: [key2, key3, key1],
contactInformation: .url(URL(string: "https://example.com")!),
expirationDuration: .hours(24),
validityDuration: .hours(12)
)
#expect(config.primaryKey == key1)
#expect(config.keys == [key1, key2])
#expect(config.deprecatedKeys == [key3])
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
#expect(config.expirationDuration == .hours(24))
#expect(config.validityDuration == .hours(12))
}
}

@Suite
struct Updates {
let key1 = try! VAPID.Key(base64URLEncoded: "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=")
let key2 = try! VAPID.Key(base64URLEncoded: "wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk=")
let key3 = try! VAPID.Key(base64URLEncoded: "bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE=")

@Test func primaryKeyOnly() throws {
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))

try config.updateKeys(primaryKey: key2, keys: [], deprecatedKeys: nil)
#expect(config.primaryKey == key2)
#expect(config.keys == [key2])
#expect(config.deprecatedKeys == nil)
}

@Test func noKeys() throws {
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: nil)
}
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: [])
}
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: [key1])
}
}

@Test func multipleKeys() throws {
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))

try config.updateKeys(primaryKey: nil, keys: [key2], deprecatedKeys: nil)
#expect(config.primaryKey == nil)
#expect(config.keys == [key2])
#expect(config.deprecatedKeys == nil)

try config.updateKeys(primaryKey: nil, keys: [key2, key3], deprecatedKeys: nil)
#expect(config.primaryKey == nil)
#expect(config.keys == [key2, key3])
#expect(config.deprecatedKeys == nil)
}

@Test func multipleAndDeprecatedKeys() throws {
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))

try config.updateKeys(primaryKey: nil, keys: [key2], deprecatedKeys: [key2, key3])
#expect(config.primaryKey == nil)
#expect(config.keys == [key2])
#expect(config.deprecatedKeys == [key3])

try config.updateKeys(primaryKey: nil, keys: [key2, key3], deprecatedKeys: [key2, key3])
#expect(config.primaryKey == nil)
#expect(config.keys == [key2, key3])
#expect(config.deprecatedKeys == nil)
}

@Test func multipleAndPrimaryKeys() throws {
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))

try config.updateKeys(primaryKey: key2, keys: [key3], deprecatedKeys: [key1, key2, key3])
#expect(config.primaryKey == key2)
#expect(config.keys == [key2, key3])
#expect(config.deprecatedKeys == [key1])

try config.updateKeys(primaryKey: key2, keys: [key3], deprecatedKeys: [key2, key3])
#expect(config.primaryKey == key2)
#expect(config.keys == [key2, key3])
#expect(config.deprecatedKeys == nil)
}
}

@Suite
struct Coding {
let key1 = try! VAPID.Key(base64URLEncoded: "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=")
let key2 = try! VAPID.Key(base64URLEncoded: "wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk=")
let key3 = try! VAPID.Key(base64URLEncoded: "bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE=")

func encode(_ configuration: VAPID.Configuration) throws -> String {
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
return String(decoding: try encoder.encode(configuration), as: UTF8.self)
}

@Test func encodesPrimaryKeyOnly() async throws {
#expect(
try encode(.init(key: key1, contactInformation: .email("[email protected]"))) ==
"""
{
"contactInformation" : "mailto:[email protected]",
"expirationDuration" : 79200,
"primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=",
"validityDuration" : 72000
}
"""
)
}

@Test func encodesMultipleKeysWithoutDuplicates() async throws {
#expect(
try encode(.init(
primaryKey: key1,
keys: [key2],
deprecatedKeys: [key1, key2, key3],
contactInformation: .email("[email protected]"),
expirationDuration: .hours(1),
validityDuration: .hours(10)
)) ==
"""
{
"contactInformation" : "mailto:[email protected]",
"deprecatedKeys" : [
"bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE="
],
"expirationDuration" : 3600,
"keys" : [
"wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk="
],
"primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=",
"validityDuration" : 36000
}
"""
)
}
}
}

@Suite("Contact Information Coding")
struct ContactInformationCoding {
@Test func encodesToString() async throws {
func encode(_ contactInformation: VAPID.Configuration.ContactInformation) throws -> String {
String(decoding: try JSONEncoder().encode(contactInformation), as: UTF8.self)
}
#expect(try encode(.email("[email protected]")) == "\"mailto:[email protected]\"")
#expect(try encode(.email("junk")) == "\"mailto:junk\"")
#expect(try encode(.email("")) == "\"mailto:\"")
#expect(try encode(.url(URL(string: "https://example.com")!)) == "\"https:\\/\\/example.com\"")
#expect(try encode(.url(URL(string: "junk")!)) == "\"junk\"")
}

@Test func decodesFromString() async throws {
func decode(_ string: String) throws -> VAPID.Configuration.ContactInformation {
try JSONDecoder().decode(VAPID.Configuration.ContactInformation.self, from: Data(string.utf8))
}
#expect(try decode("\"mailto:[email protected]\"") == .email("[email protected]"))
#expect(try decode("\"mailto:junk\"") == .email("junk"))
#expect(try decode("\"https://example.com\"") == .url(URL(string: "https://example.com")!))
#expect(try decode("\"HTTP://example.com\"") == .url(URL(string: "HTTP://example.com")!))

#expect(throws: DecodingError.self) {
try decode("\"\"")
}

#expect(throws: DecodingError.self) {
try decode("\"junk\"")
}

#expect(throws: DecodingError.self) {
try decode("\"file:///Users/you/Library\"")
}

#expect(throws: DecodingError.self) {
try decode("\"mailto:\"")
}
}
}
31 changes: 31 additions & 0 deletions Tests/WebPushTests/VAPIDTokenTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@ struct MockVAPIDKey<Bytes: ContiguousBytes & Sendable>: VAPIDKeyProtocol {
}

@Suite struct VAPIDTokenTests {
@Test func expirationProperlyConfigured() throws {
let date = Date(timeIntervalSince1970: 1_234_567)
let token = VAPID.Token(
origin: "https://push.example.net",
contactInformation: .email("[email protected]"),
expiration: date
)

#expect(token.expiration == 1_234_567)
}

@Test func generatesValidSignedToken() throws {
let key = VAPID.Key()

Expand Down Expand Up @@ -66,4 +77,24 @@ struct MockVAPIDKey<Bytes: ContiguousBytes & Sendable>: VAPIDKeyProtocol {
let generatedHeader = try expectedToken.generateAuthorization(signedBy: mockKey)
#expect(generatedHeader == "vapid t=eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA, k=BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
}

@Test func invalidTokenInitialization() {
let invalidToken = VAPID.Token(token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA", key: "")
#expect(invalidToken == nil)

let incompleteToken = VAPID.Token(token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBs", key: "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
#expect(incompleteToken == nil)

let invalidTokenHeader = VAPID.Token(token: "eyJ0eXAiOiJKV1QiL CJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA", key: "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
#expect(invalidTokenHeader == nil)

let invalidTokenBody = VAPID.Token(token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHA iOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA", key: "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
#expect(invalidTokenBody == nil)

let invalidTokenSignature = VAPID.Token(token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK 9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA", key: "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
#expect(invalidTokenSignature == nil)

let invalidTokenKey = VAPID.Token(token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiJ9.eyJhdWQiOiJodHRwczovL3B1c2guZXhhbXBsZS5uZXQiLCJleHAiOjE0NTM1MjM3NjgsInN1YiI6Im1haWx0bzpwdXNoQGV4YW1wbGUuY29tIn0.i3CYb7t4xfxCDquptFOepC9GAu_HLGkMlMuCGSK2rpiUfnK9ojFwDXb1JrErtmysazNjjvW2L9OkSSHzvoD1oA", key: "BA1Hxzyi1RUM1b5wjxsn7nGxAszw2u61m164i3MrAIxHF6 YK5h4SDYic-dRuU_RCPCfA5aq9ojSwk5Y2EmClBPs")
#expect(invalidTokenKey == nil)
}
}

0 comments on commit 06962c3

Please sign in to comment.