11import Foundation
22import Testing
3-
4- private struct TestSnapshotCorrelation : Hashable {
5- let requestID : [ UInt8 ]
6- let gatewayID : [ UInt8 ]
7-
8- init ( requestID: String , gatewayID: String ) {
9- self . requestID = Array ( requestID. utf8)
10- self . gatewayID = Array ( gatewayID. utf8)
11- }
12- }
3+ @testable import OpenClaw
134
145struct WatchApprovalTransportSourceGuardTests {
156 @Test func `watch approval loading and screenshot proof are visible`() throws {
@@ -122,17 +113,6 @@ struct WatchApprovalTransportSourceGuardTests {
122113 " WatchGatewayID.key(snapshot.gatewayStableID) == WatchGatewayID.key(token.gatewayStableID) " ) )
123114 }
124115
125- @Test func `lost requested snapshot ignores unrelated accepted snapshots`() {
126- let requested = TestSnapshotCorrelation ( requestID: " request-a " , gatewayID: " gateway-a " )
127- let unrelatedRequest = TestSnapshotCorrelation ( requestID: " request-b " , gatewayID: " gateway-a " )
128- let unrelatedOwner = TestSnapshotCorrelation ( requestID: " request-a " , gatewayID: " gateway-b " )
129- var accepted : Set < TestSnapshotCorrelation > = [ unrelatedRequest, unrelatedOwner]
130-
131- #expect( accepted. remove ( requested) == nil )
132- accepted. insert ( requested)
133- #expect( accepted. remove ( requested) == requested)
134- }
135-
136116 @Test func `watch applies retry reset only to its exact active attempt`() throws {
137117 let storeSource = try Self . readWatchSource ( " WatchInboxStore.swift " )
138118 let promptConsume = try Self . extract (
@@ -176,10 +156,11 @@ struct WatchApprovalTransportSourceGuardTests {
176156 #expect( !parser. contains ( " ?? [] " ) )
177157 }
178158
179- @Test func `watch approval ids remain exact opaque values `() throws {
159+ @Test func `watch reuses exact compound identifier policy `() throws {
180160 let receiverSource = try Self . readWatchSource ( " WatchConnectivityReceiver.swift " )
181161 let storeSource = try Self . readWatchSource ( " WatchInboxStore.swift " )
182162 let messagesSource = try Self . readWatchSource ( " WatchInboxMessages.swift " )
163+ let viewSource = try Self . readWatchSource ( " WatchInboxView.swift " )
183164 let parser = try Self . extract (
184165 receiverSource,
185166 from: " private static func parseExecApprovalItem( " ,
@@ -196,20 +177,13 @@ struct WatchApprovalTransportSourceGuardTests {
196177 storeSource,
197178 from: " private func restorePersistedState() " ,
198179 to: " private func persistState() " )
199- // The identity validators live in WatchInboxMessages.swift since the
200- // message/model types were split out of WatchInboxStore.swift.
201- let approvalValidator = try Self . extract (
202- messagesSource,
203- from: " enum WatchApprovalID { " ,
204- to: " enum WatchGatewayID { " )
205- let gatewayValidator = try Self . extract (
206- messagesSource,
207- from: " enum WatchGatewayID { " ,
208- to: " struct WatchExecApprovalIdentityKey: " )
209-
210180 let prefixed = " \u{001C} approval "
211- #expect( prefixed != " approval " )
212- #expect( Array ( prefixed. utf8) != Array ( " approval " . utf8) )
181+ #expect( ExecApprovalIdentifier . exact ( prefixed) == prefixed)
182+ #expect( ExecApprovalIdentifier . exact ( " approval " ) == " approval " )
183+ #expect( ExecApprovalIdentifier . exact ( " . " ) == nil )
184+ #expect( GatewayStableIdentifier . exact ( " gateway " ) == " gateway " )
185+ #expect( messagesSource. contains ( " typealias WatchApprovalID = ExecApprovalIdentifier " ) )
186+ #expect( messagesSource. contains ( " typealias WatchGatewayID = GatewayStableIdentifier " ) )
213187 #expect( parser. contains ( " WatchApprovalID.exact(payload[ \" id \" ] as? String) " ) )
214188 #expect( !parser. contains ( " id = (payload[ \" id \" ] as? String)?.trimmingCharacters " ) )
215189 #expect( ownerKey. contains ( " WatchApprovalID.key(approvalId) " ) )
@@ -218,26 +192,10 @@ struct WatchApprovalTransportSourceGuardTests {
218192 #expect( snapshotConsume. contains ( " let hasCanonicalRequestCorrelation = " ) )
219193 #expect( snapshotConsume. contains ( " guard hasCanonicalRequestCorrelation else { return true } " ) )
220194 #expect( restore. contains ( " WatchApprovalID.exact(record.approvalID) != nil " ) )
221- #expect( approvalValidator. contains ( " !value.isEmpty " ) )
222- #expect( approvalValidator. contains ( " value != \" . \" , " ) )
223- #expect( approvalValidator. contains ( " value != \" .. \" " ) )
224- #expect( !approvalValidator. contains ( " trimmingCharacters " ) )
225- #expect( !approvalValidator. contains ( " isECMAScriptTrimScalar " ) )
226- #expect( gatewayValidator. contains ( " guard let value, !value.isEmpty " ) )
227- #expect( !gatewayValidator. contains ( " WatchApprovalID.exact " ) )
228- #expect( !gatewayValidator. contains ( " value != \" . \" " ) )
229- #expect( !gatewayValidator. contains ( " trimmingCharacters " ) )
230- #expect( Array ( " approval " . utf8) != Array ( " approval " . utf8) )
231- }
232-
233- @Test func `watch canonical- equivalent approval IDs remain independently targetable`() throws {
234- let storeSource = try Self . readWatchSource ( " WatchInboxStore.swift " )
235- let messagesSource = try Self . readWatchSource ( " WatchInboxMessages.swift " )
236- let viewSource = try Self . readWatchSource ( " WatchInboxView.swift " )
237195 let composedID = " approval- \u{00E9} "
238196 let decomposedID = " approval-e \u{0301} "
239- let composedKey = Data ( composedID. utf8 )
240- let decomposedKey = Data ( decomposedID. utf8 )
197+ let composedKey = ExactOpaqueIdentifierKey ( composedID)
198+ let decomposedKey = ExactOpaqueIdentifierKey ( decomposedID)
241199 #expect( composedID == decomposedID)
242200 #expect( composedKey != decomposedKey)
243201
@@ -246,16 +204,19 @@ struct WatchApprovalTransportSourceGuardTests {
246204 let remainingID = try #require( pending [ decomposedKey] )
247205 #expect( Array ( remainingID. utf8) == Array ( decomposedID. utf8) )
248206
249- // Byte-exact identity types live in WatchInboxMessages.swift after the split.
250- #expect( messagesSource. contains ( " self.bytes = Array(rawValue.utf8) " ) )
207+ #expect( messagesSource. contains ( " typealias WatchOpaqueUTF8Key = ExactOpaqueIdentifierKey " ) )
251208 #expect( messagesSource. contains ( " var id: WatchExecApprovalIdentityKey " ) )
252209 #expect( messagesSource. contains ( " var approvalID: WatchApprovalID.Key " ) )
253210 #expect( messagesSource. contains ( " var gatewayID: WatchGatewayID.Key " ) )
254211 #expect( storeSource. contains ( " WatchApprovalID.key(tombstone.approvalId) == key.approvalID " ) )
212+ #expect( storeSource. contains ( " WatchGatewayID.key(tombstone.gatewayStableID) == key.gatewayID " ) )
255213 #expect( storeSource. contains ( " approvalKey.notificationComponent " ) )
214+ #expect( storeSource. contains ( " gatewayKey.notificationComponent " ) )
256215 #expect( !storeSource. contains ( " record.id == approval.id " ) )
257216 #expect( !messagesSource. contains ( " record.id == approval.id " ) )
258217 #expect( !storeSource. contains ( " tombstone.approvalId == key.approvalId " ) )
218+ #expect( receiverSource. contains ( " WatchGatewayID.exact(payload[ \" gatewayStableID \" ] as? String) " ) )
219+ #expect( !receiverSource. contains ( " gatewayStableID?.trimmingCharacters " ) )
259220 #expect( viewSource. contains ( " record.approvalID " ) )
260221 #expect( viewSource. contains ( " $0.id == self.record.id " ) )
261222 }
@@ -316,32 +277,10 @@ struct WatchApprovalTransportSourceGuardTests {
316277 #expect( detailScroll. contains ( " self.content " ) )
317278 }
318279
319- @Test func `watch compounds exact owner and approval identity`() throws {
320- let storeSource = try Self . readWatchSource ( " WatchInboxStore.swift " )
321- let messagesSource = try Self . readWatchSource ( " WatchInboxMessages.swift " )
322- let receiverSource = try Self . readWatchSource ( " WatchConnectivityReceiver.swift " )
323- let sameApprovalID = Data ( " approval-same " . utf8)
324- let composedOwner = Data ( " gateway- \u{00E9} " . utf8)
325- let decomposedOwner = Data ( " gateway-e \u{0301} " . utf8)
326- #expect( composedOwner != decomposedOwner)
327- #expect( Set ( [ [ composedOwner, sameApprovalID] , [ decomposedOwner, sameApprovalID] ] ) . count == 2 )
328-
329- // The compound identity key type lives in WatchInboxMessages.swift after the split.
330- #expect( messagesSource. contains ( " struct WatchExecApprovalIdentityKey: Hashable " ) )
331- #expect( storeSource. contains ( " selectedExecApprovalGatewayStableID " ) )
332- #expect( storeSource. contains ( " gatewayKey.notificationComponent " ) )
333- #expect( storeSource. contains ( " WatchGatewayID.key(tombstone.gatewayStableID) == key.gatewayID " ) )
334- #expect( storeSource. contains ( " \" watch.execApproval. \\ (record.approvalID) \" " ) )
335- #expect( receiverSource. contains ( " WatchGatewayID.exact(payload[ \" gatewayStableID \" ] as? String) " ) )
336- #expect( !receiverSource. contains ( " gatewayStableID?.trimmingCharacters " ) )
337- #expect( !storeSource. contains ( " isECMAScriptTrimScalar " ) )
338- #expect( !messagesSource. contains ( " isECMAScriptTrimScalar " ) )
339- #expect( Array ( " \u{0085} gateway " . utf8) != Array ( " gateway " . utf8) )
340- }
341-
342280 @Test func `watch notification identity frames do tted components`() throws {
343281 let storeSource = try Self . readWatchSource ( " WatchInboxStore.swift " )
344282 let messagesSource = try Self . readWatchSource ( " WatchInboxMessages.swift " )
283+ let identifierSource = try Self . readIOSServiceSource ( " ExactOpaqueIdentifier.swift " )
345284 let promptConsume = try Self . extract (
346285 storeSource,
347286 from: " func consume( \n execApprovalPrompt " ,
@@ -350,12 +289,14 @@ struct WatchApprovalTransportSourceGuardTests {
350289 let rawRight = " a " + " . " + " b.c "
351290 #expect( rawLeft == rawRight)
352291
353- let framedLeft = Self . notificationComponent ( " a.b " ) + " . " + Self. notificationComponent ( " c " )
354- let framedRight = Self . notificationComponent ( " a " ) + " . " + Self. notificationComponent ( " b.c " )
292+ let framedLeft = ExactOpaqueIdentifierKey ( " a.b " ) . notificationComponent + " . "
293+ + ExactOpaqueIdentifierKey( " c " ) . notificationComponent
294+ let framedRight = ExactOpaqueIdentifierKey ( " a " ) . notificationComponent + " . "
295+ + ExactOpaqueIdentifierKey( " b.c " ) . notificationComponent
355296 #expect( framedLeft != framedRight)
356- // The notification-component percent encoder lives in WatchInboxMessages.swift.
357- #expect( messagesSource . contains ( " 0x2D, 0x5F, 0x7E " ) )
358- #expect( !messagesSource . contains ( " 0x2D, 0x2E, 0x5F, 0x7E " ) )
297+ #expect ( messagesSource . contains ( " typealias WatchOpaqueUTF8Key = ExactOpaqueIdentifierKey " ) )
298+ #expect( identifierSource . contains ( " 0x2D, 0x5F, 0x7E " ) )
299+ #expect( !identifierSource . contains ( " 0x2D, 0x2E, 0x5F, 0x7E " ) )
359300 #expect( !storeSource. contains ( " 0x2D, 0x2E, 0x5F, 0x7E " ) )
360301 #expect( storeSource. contains ( " gatewayKey.notificationComponent). \\ (approvalKey.notificationComponent " ) )
361302 #expect( storeSource. contains ( " legacyExecApprovalNotificationIdentifier " ) )
@@ -412,22 +353,6 @@ struct WatchApprovalTransportSourceGuardTests {
412353 " Self.gatewayIDsMatch(approval.gatewayStableID, snapshotGatewayID) " ) )
413354 }
414355
415- private static func notificationComponent( _ rawValue: String ) -> String {
416- let hexDigits = Array ( " 0123456789ABCDEF " . utf8)
417- var encoded : [ UInt8 ] = [ ]
418- for byte in rawValue. utf8 {
419- switch byte {
420- case 0x30 ... 0x39 , 0x41 ... 0x5A , 0x61 ... 0x7A , 0x2D , 0x5F , 0x7E :
421- encoded. append ( byte)
422- default :
423- encoded. append ( 0x25 )
424- encoded. append ( hexDigits [ Int ( byte >> 4 ) ] )
425- encoded. append ( hexDigits [ Int ( byte & 0x0F ) ] )
426- }
427- }
428- return String ( decoding: encoded, as: UTF8 . self)
429- }
430-
431356 private static func readWatchSource( _ filename: String ) throws -> String {
432357 let url = URL ( fileURLWithPath: #filePath)
433358 . deletingLastPathComponent ( )
@@ -437,6 +362,15 @@ struct WatchApprovalTransportSourceGuardTests {
437362 return try String ( contentsOf: url, encoding: . utf8)
438363 }
439364
365+ private static func readIOSServiceSource( _ filename: String ) throws -> String {
366+ let url = URL ( fileURLWithPath: #filePath)
367+ . deletingLastPathComponent ( )
368+ . deletingLastPathComponent ( )
369+ . appendingPathComponent ( " Sources/Services " )
370+ . appendingPathComponent ( filename)
371+ return try String ( contentsOf: url, encoding: . utf8)
372+ }
373+
440374 private static func extract( _ source: String , from start: String , to end: String ) throws -> String {
441375 let startRange = try #require( source. range ( of: start) )
442376 let tail = source [ startRange. lowerBound... ]
0 commit comments