Skip to content

Commit bf0d6f0

Browse files
Improved code coverage from 34% to 50%
1 parent a8bcb64 commit bf0d6f0

File tree

3 files changed

+382
-11
lines changed

3 files changed

+382
-11
lines changed

Tests/WebPushTests/Base64URLCodingTests.swift

+21-11
Original file line numberDiff line numberDiff line change
@@ -10,16 +10,26 @@ import Foundation
1010
import Testing
1111
@testable import WebPush
1212

13-
@Test func base64URLDecoding() async throws {
14-
let string = ">>> Hello, swift-webpush world??? 🎉"
15-
let base64Encoded = "Pj4+IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8/IPCfjok="
16-
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
17-
#expect(String(decoding: Data(base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
18-
#expect(String(decoding: Data(base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
19-
}
13+
@Suite("Base 64 URL Coding")
14+
struct Base64URLCoding {
15+
@Test func base64URLDecoding() async throws {
16+
let string = ">>> Hello, swift-webpush world??? 🎉"
17+
let base64Encoded = "Pj4+IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8/IPCfjok="
18+
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
19+
#expect(String(decoding: Data(base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
20+
#expect(String(decoding: Data(base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
21+
#expect(String(decoding: [UInt8](base64URLEncoded: base64Encoded)!, as: UTF8.self) == string)
22+
#expect(String(decoding: [UInt8](base64URLEncoded: base64URLEncoded)!, as: UTF8.self) == string)
23+
}
24+
25+
@Test func invalidBase64URLDecoding() async throws {
26+
#expect(Data(base64URLEncoded: " ") == nil)
27+
}
2028

21-
@Test func base64URLEncoding() async throws {
22-
let string = ">>> Hello, swift-webpush world??? 🎉"
23-
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
24-
#expect(Array(string.utf8).base64URLEncodedString() == base64URLEncoded)
29+
@Test func base64URLEncoding() async throws {
30+
let string = ">>> Hello, swift-webpush world??? 🎉"
31+
let base64URLEncoded = "Pj4-IEhlbGxvLCBzd2lmdC13ZWJwdXNoIHdvcmxkPz8_IPCfjok"
32+
#expect([UInt8](string.utf8).base64URLEncodedString() == base64URLEncoded)
33+
#expect(Data(string.utf8).base64URLEncodedString() == base64URLEncoded)
34+
}
2535
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,330 @@
1+
//
2+
// VAPIDConfigurationTests.swift
3+
// swift-webpush
4+
//
5+
// Created by Dimitri Bouniol on 2024-12-15.
6+
// Copyright © 2024 Mochi Development, Inc. All rights reserved.
7+
//
8+
9+
import Crypto
10+
import Foundation
11+
import Testing
12+
@testable import WebPush
13+
14+
@Suite("VAPID Configuration Tests")
15+
struct VAPIDConfigurationTests {
16+
@Suite
17+
struct Initialization {
18+
let key1 = VAPID.Key()
19+
let key2 = VAPID.Key()
20+
let key3 = VAPID.Key()
21+
22+
@Test func primaryKeyOnly() {
23+
let config = VAPID.Configuration(
24+
key: key1,
25+
contactInformation: .email("[email protected]")
26+
)
27+
#expect(config.primaryKey == key1)
28+
#expect(config.keys == [key1])
29+
#expect(config.deprecatedKeys == nil)
30+
#expect(config.contactInformation == .email("[email protected]"))
31+
#expect(config.expirationDuration == .hours(22))
32+
#expect(config.validityDuration == .hours(20))
33+
}
34+
35+
@Test func emptyDeprecatedKeys() {
36+
let config = VAPID.Configuration(
37+
key: key1,
38+
deprecatedKeys: [],
39+
contactInformation: .url(URL(string: "https://example.com")!),
40+
expirationDuration: .hours(24),
41+
validityDuration: .hours(12)
42+
)
43+
#expect(config.primaryKey == key1)
44+
#expect(config.keys == [key1])
45+
#expect(config.deprecatedKeys == nil)
46+
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
47+
#expect(config.expirationDuration == .hours(24))
48+
#expect(config.validityDuration == .hours(12))
49+
}
50+
51+
@Test func deprecatedKeys() {
52+
let config = VAPID.Configuration(
53+
key: key1,
54+
deprecatedKeys: [key2, key3],
55+
contactInformation: .email("[email protected]")
56+
)
57+
#expect(config.primaryKey == key1)
58+
#expect(config.keys == [key1])
59+
#expect(config.deprecatedKeys == [key2, key3])
60+
#expect(config.contactInformation == .email("[email protected]"))
61+
#expect(config.expirationDuration == .hours(22))
62+
#expect(config.validityDuration == .hours(20))
63+
}
64+
65+
@Test func deprecatedAndPrimaryKeys() {
66+
let config = VAPID.Configuration(
67+
key: key1,
68+
deprecatedKeys: [key2, key3, key1],
69+
contactInformation: .url(URL(string: "https://example.com")!),
70+
expirationDuration: .hours(24),
71+
validityDuration: .hours(12)
72+
)
73+
#expect(config.primaryKey == key1)
74+
#expect(config.keys == [key1])
75+
#expect(config.deprecatedKeys == [key2, key3])
76+
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
77+
#expect(config.expirationDuration == .hours(24))
78+
#expect(config.validityDuration == .hours(12))
79+
}
80+
81+
@Test func multipleKeys() throws {
82+
let config = try VAPID.Configuration(
83+
primaryKey: nil,
84+
keys: [key1, key2],
85+
deprecatedKeys: nil,
86+
contactInformation: .email("[email protected]")
87+
)
88+
#expect(config.primaryKey == nil)
89+
#expect(config.keys == [key1, key2])
90+
#expect(config.deprecatedKeys == nil)
91+
#expect(config.contactInformation == .email("[email protected]"))
92+
#expect(config.expirationDuration == .hours(22))
93+
#expect(config.validityDuration == .hours(20))
94+
}
95+
96+
@Test func noKeys() throws {
97+
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
98+
try VAPID.Configuration(
99+
primaryKey: nil,
100+
keys: [],
101+
deprecatedKeys: [key2, key3],
102+
contactInformation: .email("[email protected]")
103+
)
104+
}
105+
}
106+
107+
@Test func multipleAndDeprecatedKeys() throws {
108+
let config = try VAPID.Configuration(
109+
primaryKey: nil,
110+
keys: [key1, key2],
111+
deprecatedKeys: [key2],
112+
contactInformation: .email("[email protected]")
113+
)
114+
#expect(config.primaryKey == nil)
115+
#expect(config.keys == [key1, key2])
116+
#expect(config.deprecatedKeys == nil)
117+
#expect(config.contactInformation == .email("[email protected]"))
118+
#expect(config.expirationDuration == .hours(22))
119+
#expect(config.validityDuration == .hours(20))
120+
}
121+
122+
@Test func multipleAndPrimaryKeys() throws {
123+
let config = try VAPID.Configuration(
124+
primaryKey: key1,
125+
keys: [key2],
126+
deprecatedKeys: [key2, key3, key1],
127+
contactInformation: .url(URL(string: "https://example.com")!),
128+
expirationDuration: .hours(24),
129+
validityDuration: .hours(12)
130+
)
131+
#expect(config.primaryKey == key1)
132+
#expect(config.keys == [key1, key2])
133+
#expect(config.deprecatedKeys == [key3])
134+
#expect(config.contactInformation == .url(URL(string: "https://example.com")!))
135+
#expect(config.expirationDuration == .hours(24))
136+
#expect(config.validityDuration == .hours(12))
137+
}
138+
}
139+
140+
@Suite
141+
struct Updates {
142+
let key1 = VAPID.Key()
143+
let key2 = VAPID.Key()
144+
let key3 = VAPID.Key()
145+
146+
@Test func primaryKeyOnly() throws {
147+
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
148+
149+
try config.updateKeys(primaryKey: key2, keys: [], deprecatedKeys: nil)
150+
#expect(config.primaryKey == key2)
151+
#expect(config.keys == [key2])
152+
#expect(config.deprecatedKeys == nil)
153+
}
154+
155+
@Test func noKeys() throws {
156+
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
157+
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
158+
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: nil)
159+
}
160+
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
161+
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: [])
162+
}
163+
#expect(throws: VAPID.ConfigurationError.keysNotProvided) {
164+
try config.updateKeys(primaryKey: nil, keys: [], deprecatedKeys: [key1])
165+
}
166+
}
167+
168+
@Test func multipleKeys() throws {
169+
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
170+
171+
try config.updateKeys(primaryKey: nil, keys: [key2], deprecatedKeys: nil)
172+
#expect(config.primaryKey == nil)
173+
#expect(config.keys == [key2])
174+
#expect(config.deprecatedKeys == nil)
175+
176+
try config.updateKeys(primaryKey: nil, keys: [key2, key3], deprecatedKeys: nil)
177+
#expect(config.primaryKey == nil)
178+
#expect(config.keys == [key2, key3])
179+
#expect(config.deprecatedKeys == nil)
180+
}
181+
182+
@Test func multipleAndDeprecatedKeys() throws {
183+
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
184+
185+
try config.updateKeys(primaryKey: nil, keys: [key2], deprecatedKeys: [key2, key3])
186+
#expect(config.primaryKey == nil)
187+
#expect(config.keys == [key2])
188+
#expect(config.deprecatedKeys == [key3])
189+
190+
try config.updateKeys(primaryKey: nil, keys: [key2, key3], deprecatedKeys: [key2, key3])
191+
#expect(config.primaryKey == nil)
192+
#expect(config.keys == [key2, key3])
193+
#expect(config.deprecatedKeys == nil)
194+
}
195+
196+
@Test func multipleAndPrimaryKeys() throws {
197+
var config = VAPID.Configuration(key: key1, contactInformation: .email("[email protected]"))
198+
199+
try config.updateKeys(primaryKey: key2, keys: [key3], deprecatedKeys: [key1, key2, key3])
200+
#expect(config.primaryKey == key2)
201+
#expect(config.keys == [key2, key3])
202+
#expect(config.deprecatedKeys == [key1])
203+
204+
try config.updateKeys(primaryKey: key2, keys: [key3], deprecatedKeys: [key2, key3])
205+
#expect(config.primaryKey == key2)
206+
#expect(config.keys == [key2, key3])
207+
#expect(config.deprecatedKeys == nil)
208+
}
209+
}
210+
211+
@Suite
212+
struct Coding {
213+
let key1 = try! VAPID.Key(base64URLEncoded: "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=")
214+
let key2 = try! VAPID.Key(base64URLEncoded: "wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk=")
215+
let key3 = try! VAPID.Key(base64URLEncoded: "bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE=")
216+
217+
func encode(_ configuration: VAPID.Configuration) throws -> String {
218+
let encoder = JSONEncoder()
219+
encoder.outputFormatting = [.prettyPrinted, .sortedKeys, .withoutEscapingSlashes]
220+
return String(decoding: try encoder.encode(configuration), as: UTF8.self)
221+
}
222+
223+
@Test func encodesPrimaryKeyOnly() async throws {
224+
#expect(
225+
try encode(.init(key: key1, contactInformation: .email("[email protected]"))) ==
226+
"""
227+
{
228+
"contactInformation" : "mailto:[email protected]",
229+
"expirationDuration" : 79200,
230+
"primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=",
231+
"validityDuration" : 72000
232+
}
233+
"""
234+
)
235+
}
236+
237+
@Test func encodesMultipleKeysWithoutDuplicates() async throws {
238+
#expect(
239+
try encode(.init(
240+
primaryKey: key1,
241+
keys: [key2],
242+
deprecatedKeys: [key1, key2, key3],
243+
contactInformation: .email("[email protected]"),
244+
expirationDuration: .hours(1),
245+
validityDuration: .hours(10)
246+
)) ==
247+
"""
248+
{
249+
"contactInformation" : "mailto:[email protected]",
250+
"deprecatedKeys" : [
251+
"bcZgo/p2WFqXaKFzmYaDKO/gARjWvGi3oXyHM2QNlfE="
252+
],
253+
"expirationDuration" : 3600,
254+
"keys" : [
255+
"wyQaGWNwvXKzVmPIhkqVQvQ+FKx1SNqHJ+re8n2ORrk="
256+
],
257+
"primaryKey" : "FniTgSrf0l+BdfeC6LiblKXBbY4LQm0S+4STNCoJI+0=",
258+
"validityDuration" : 36000
259+
}
260+
"""
261+
)
262+
}
263+
//
264+
// @Test func decodesFromString() async throws {
265+
// func decode(_ string: String) throws -> VAPID.Configuration.ContactInformation {
266+
// try JSONDecoder().decode(VAPID.Configuration.ContactInformation.self, from: Data(string.utf8))
267+
// }
268+
// #expect(try decode("\"mailto:[email protected]\"") == .email("[email protected]"))
269+
// #expect(try decode("\"mailto:junk\"") == .email("junk"))
270+
// #expect(try decode("\"https://example.com\"") == .url(URL(string: "https://example.com")!))
271+
// #expect(try decode("\"HTTP://example.com\"") == .url(URL(string: "HTTP://example.com")!))
272+
//
273+
// #expect(throws: DecodingError.self) {
274+
// try decode("\"\"")
275+
// }
276+
//
277+
// #expect(throws: DecodingError.self) {
278+
// try decode("\"junk\"")
279+
// }
280+
//
281+
// #expect(throws: DecodingError.self) {
282+
// try decode("\"file:///Users/you/Library\"")
283+
// }
284+
//
285+
// #expect(throws: DecodingError.self) {
286+
// try decode("\"mailto:\"")
287+
// }
288+
// }
289+
}
290+
}
291+
292+
@Suite("Contact Information Coding")
293+
struct ContactInformationCoding {
294+
@Test func encodesToString() async throws {
295+
func encode(_ contactInformation: VAPID.Configuration.ContactInformation) throws -> String {
296+
String(decoding: try JSONEncoder().encode(contactInformation), as: UTF8.self)
297+
}
298+
#expect(try encode(.email("[email protected]")) == "\"mailto:[email protected]\"")
299+
#expect(try encode(.email("junk")) == "\"mailto:junk\"")
300+
#expect(try encode(.email("")) == "\"mailto:\"")
301+
#expect(try encode(.url(URL(string: "https://example.com")!)) == "\"https:\\/\\/example.com\"")
302+
#expect(try encode(.url(URL(string: "junk")!)) == "\"junk\"")
303+
}
304+
305+
@Test func decodesFromString() async throws {
306+
func decode(_ string: String) throws -> VAPID.Configuration.ContactInformation {
307+
try JSONDecoder().decode(VAPID.Configuration.ContactInformation.self, from: Data(string.utf8))
308+
}
309+
#expect(try decode("\"mailto:[email protected]\"") == .email("[email protected]"))
310+
#expect(try decode("\"mailto:junk\"") == .email("junk"))
311+
#expect(try decode("\"https://example.com\"") == .url(URL(string: "https://example.com")!))
312+
#expect(try decode("\"HTTP://example.com\"") == .url(URL(string: "HTTP://example.com")!))
313+
314+
#expect(throws: DecodingError.self) {
315+
try decode("\"\"")
316+
}
317+
318+
#expect(throws: DecodingError.self) {
319+
try decode("\"junk\"")
320+
}
321+
322+
#expect(throws: DecodingError.self) {
323+
try decode("\"file:///Users/you/Library\"")
324+
}
325+
326+
#expect(throws: DecodingError.self) {
327+
try decode("\"mailto:\"")
328+
}
329+
}
330+
}

0 commit comments

Comments
 (0)