-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Improved code coverage from 34% to 50%
- Loading branch information
1 parent
a8bcb64
commit 06962c3
Showing
3 changed files
with
356 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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:\"") | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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() | ||
|
||
|
@@ -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) | ||
} | ||
} |