@@ -44,14 +44,17 @@ struct LicenseTests {
4444 machineId: " machine1 " ,
4545 signedPayload: SignedLicensePayload (
4646 data: LicensePayloadData (
47+ billingCycle: nil ,
4748 licenseKey: " test-key " ,
4849 email: " test@test.com " ,
4950 status: " active " ,
5051 expiresAt: nil ,
51- issuedAt: " 2024-01-01T00:00:00Z "
52+ issuedAt: " 2024-01-01T00:00:00Z " ,
53+ tier: " starter "
5254 ) ,
5355 signature: " sig "
54- )
56+ ) ,
57+ tier: " starter "
5558 )
5659 #expect( license. isExpired == false )
5760 }
@@ -68,14 +71,17 @@ struct LicenseTests {
6871 machineId: " machine1 " ,
6972 signedPayload: SignedLicensePayload (
7073 data: LicensePayloadData (
74+ billingCycle: nil ,
7175 licenseKey: " test-key " ,
7276 email: " test@test.com " ,
7377 status: " active " ,
7478 expiresAt: " 2025-01-01T00:00:00Z " ,
75- issuedAt: " 2024-01-01T00:00:00Z "
79+ issuedAt: " 2024-01-01T00:00:00Z " ,
80+ tier: " starter "
7681 ) ,
7782 signature: " sig "
78- )
83+ ) ,
84+ tier: " starter "
7985 )
8086 #expect( license. isExpired == false )
8187 }
@@ -92,14 +98,17 @@ struct LicenseTests {
9298 machineId: " machine1 " ,
9399 signedPayload: SignedLicensePayload (
94100 data: LicensePayloadData (
101+ billingCycle: nil ,
95102 licenseKey: " test-key " ,
96103 email: " test@test.com " ,
97104 status: " expired " ,
98105 expiresAt: " 2024-01-01T00:00:00Z " ,
99- issuedAt: " 2023-01-01T00:00:00Z "
106+ issuedAt: " 2023-01-01T00:00:00Z " ,
107+ tier: " starter "
100108 ) ,
101109 signature: " sig "
102- )
110+ ) ,
111+ tier: " starter "
103112 )
104113 #expect( license. isExpired == true )
105114 }
@@ -117,14 +126,17 @@ struct LicenseTests {
117126 machineId: " machine1 " ,
118127 signedPayload: SignedLicensePayload (
119128 data: LicensePayloadData (
129+ billingCycle: nil ,
120130 licenseKey: " test-key " ,
121131 email: " test@test.com " ,
122132 status: " active " ,
123133 expiresAt: nil ,
124- issuedAt: " 2024-01-01T00:00:00Z "
134+ issuedAt: " 2024-01-01T00:00:00Z " ,
135+ tier: " starter "
125136 ) ,
126137 signature: " sig "
127- )
138+ ) ,
139+ tier: " starter "
128140 )
129141 #expect( license. daysSinceLastValidation == 0 )
130142 }
@@ -144,14 +156,17 @@ struct LicenseTests {
144156 machineId: " machine1 " ,
145157 signedPayload: SignedLicensePayload (
146158 data: LicensePayloadData (
159+ billingCycle: nil ,
147160 licenseKey: " test-key " ,
148161 email: " test@test.com " ,
149162 status: " active " ,
150163 expiresAt: nil ,
151- issuedAt: " 2024-01-01T00:00:00Z "
164+ issuedAt: " 2024-01-01T00:00:00Z " ,
165+ tier: " starter "
152166 ) ,
153167 signature: " sig "
154- )
168+ ) ,
169+ tier: " starter "
155170 )
156171 #expect( license. daysSinceLastValidation == 5 )
157172 }
@@ -161,11 +176,13 @@ struct LicenseTests {
161176 @Test ( " License.from maps active status correctly " )
162177 func licenseFromMapsActiveStatus( ) {
163178 let payloadData = LicensePayloadData (
179+ billingCycle: nil ,
164180 licenseKey: " test-key " ,
165181 email: " test@test.com " ,
166182 status: " active " ,
167183 expiresAt: nil ,
168- issuedAt: " 2024-01-01T00:00:00Z "
184+ issuedAt: " 2024-01-01T00:00:00Z " ,
185+ tier: " starter "
169186 )
170187 let signedPayload = SignedLicensePayload ( data: payloadData, signature: " sig " )
171188 let license = License . from (
@@ -179,11 +196,13 @@ struct LicenseTests {
179196 @Test ( " License.from maps expired status correctly " )
180197 func licenseFromMapsExpiredStatus( ) {
181198 let payloadData = LicensePayloadData (
199+ billingCycle: " monthly " ,
182200 licenseKey: " test-key " ,
183201 email: " test@test.com " ,
184202 status: " expired " ,
185203 expiresAt: " 2024-01-01T00:00:00Z " ,
186- issuedAt: " 2023-01-01T00:00:00Z "
204+ issuedAt: " 2023-01-01T00:00:00Z " ,
205+ tier: " starter "
187206 )
188207 let signedPayload = SignedLicensePayload ( data: payloadData, signature: " sig " )
189208 let license = License . from (
@@ -197,11 +216,13 @@ struct LicenseTests {
197216 @Test ( " License.from maps suspended status correctly " )
198217 func licenseFromMapsSuspendedStatus( ) {
199218 let payloadData = LicensePayloadData (
219+ billingCycle: nil ,
200220 licenseKey: " test-key " ,
201221 email: " test@test.com " ,
202222 status: " suspended " ,
203223 expiresAt: nil ,
204- issuedAt: " 2024-01-01T00:00:00Z "
224+ issuedAt: " 2024-01-01T00:00:00Z " ,
225+ tier: " starter "
205226 )
206227 let signedPayload = SignedLicensePayload ( data: payloadData, signature: " sig " )
207228 let license = License . from (
@@ -215,11 +236,13 @@ struct LicenseTests {
215236 @Test ( " License.from maps unknown status to validationFailed " )
216237 func licenseFromMapsUnknownStatusToValidationFailed( ) {
217238 let payloadData = LicensePayloadData (
239+ billingCycle: nil ,
218240 licenseKey: " test-key " ,
219241 email: " test@test.com " ,
220242 status: " unknown " ,
221243 expiresAt: nil ,
222- issuedAt: " 2024-01-01T00:00:00Z "
244+ issuedAt: " 2024-01-01T00:00:00Z " ,
245+ tier: " starter "
223246 )
224247 let signedPayload = SignedLicensePayload ( data: payloadData, signature: " sig " )
225248 let license = License . from (
@@ -229,4 +252,64 @@ struct LicenseTests {
229252 )
230253 #expect( license. status == . validationFailed)
231254 }
255+
256+ // MARK: - LicensePayloadData Encoding Tests
257+
258+ @Test ( " LicensePayloadData encodes all 7 fields in alphabetical order matching server format " )
259+ func payloadDataEncodesAllFieldsAlphabetically( ) throws {
260+ let payloadData = LicensePayloadData (
261+ billingCycle: " monthly " ,
262+ licenseKey: " ABC-123 " ,
263+ email: " user@example.com " ,
264+ status: " active " ,
265+ expiresAt: " 2025-12-31T23:59:59Z " ,
266+ issuedAt: " 2025-01-01T00:00:00Z " ,
267+ tier: " pro "
268+ )
269+ let encoder = JSONEncoder ( )
270+ encoder. outputFormatting = [ . sortedKeys]
271+ let data = try encoder. encode ( payloadData)
272+ let json = String ( data: data, encoding: . utf8)
273+
274+ guard let json else {
275+ Issue . record ( " Failed to encode payload data to UTF-8 string " )
276+ return
277+ }
278+
279+ guard let keys = try JSONSerialization . jsonObject ( with: data) as? [ String : Any ] else {
280+ Issue . record ( " Failed to deserialize JSON as dictionary " )
281+ return
282+ }
283+
284+ let expectedKeys = [ " billing_cycle " , " email " , " expires_at " , " issued_at " , " license_key " , " status " , " tier " ]
285+ #expect( keys. keys. sorted ( ) == expectedKeys)
286+
287+ let billingCycleRange = json. range ( of: " billing_cycle " )
288+ let tierRange = json. range ( of: " tier " )
289+ guard let billingCycleRange, let tierRange else {
290+ Issue . record ( " Expected keys not found in JSON string " )
291+ return
292+ }
293+ #expect( billingCycleRange. lowerBound < tierRange. lowerBound)
294+ }
295+
296+ @Test ( " LicensePayloadData encodes nil billingCycle as null " )
297+ func payloadDataEncodesNilBillingCycleAsNull( ) throws {
298+ let payloadData = LicensePayloadData (
299+ billingCycle: nil ,
300+ licenseKey: " ABC-123 " ,
301+ email: " user@example.com " ,
302+ status: " active " ,
303+ expiresAt: nil ,
304+ issuedAt: " 2025-01-01T00:00:00Z " ,
305+ tier: " starter "
306+ )
307+ let encoder = JSONEncoder ( )
308+ encoder. outputFormatting = [ . sortedKeys]
309+ let data = try encoder. encode ( payloadData)
310+ let json = String ( data: data, encoding: . utf8)
311+
312+ #expect( json? . contains ( " \" billing_cycle \" :null " ) == true )
313+ #expect( json? . contains ( " \" expires_at \" :null " ) == true )
314+ }
232315}
0 commit comments