@@ -89,13 +89,15 @@ describe('PluginUpdatePlatform legacy UUID migration', () => {
8989 const legacyUpdateSensorUuid = mockApi . hap . uuid . generate ( LEGACY_UPDATE_SENSOR_UUID_KEY )
9090
9191 let cachedLegacyUpdateAccessory : any = undefined
92+ let cachedCurrentUpdateAccessory : any = undefined
9293 let updateSensorCached = false
9394
9495 // Simulate configureAccessory for each cached accessory
9596 for ( const accessory of cachedAccessories ) {
9697 if ( accessory . UUID === updateSensorUuid ) {
9798 mockUpdateSensor . configureAccessory ( accessory )
9899 updateSensorCached = true
100+ cachedCurrentUpdateAccessory = accessory
99101 continue
100102 }
101103 if ( accessory . UUID === legacyUpdateSensorUuid ) {
@@ -112,9 +114,15 @@ describe('PluginUpdatePlatform legacy UUID migration', () => {
112114 if ( ! cachedLegacyUpdateAccessory ) return
113115
114116 if ( updateSensorCached ) {
115- mockApi . unregisterPlatformAccessories ( PLUGIN_NAME , PLATFORM_NAME , [ cachedLegacyUpdateAccessory ] )
117+ // Both UUIDs found — remove the newer empty accessory, promote the legacy one
118+ // so the user's HomeKit customisations are preserved.
119+ if ( cachedCurrentUpdateAccessory ) {
120+ mockApi . unregisterPlatformAccessories ( PLUGIN_NAME , PLATFORM_NAME , [ cachedCurrentUpdateAccessory ] )
121+ cachedCurrentUpdateAccessory = undefined
122+ }
123+ mockUpdateSensor . configureAccessory ( cachedLegacyUpdateAccessory )
116124 cachedLegacyUpdateAccessory = undefined
117- mockLog . info ( 'Removed stale legacy update sensor accessory (migrated to current UUID )' )
125+ mockLog . info ( 'Migrated update sensor to legacy cached accessory (preserved HomeKit customisations )' )
118126 return
119127 }
120128
@@ -160,21 +168,24 @@ describe('PluginUpdatePlatform legacy UUID migration', () => {
160168 expect ( registered ) . toBe ( true )
161169 } )
162170
163- it ( 'Scenario B: both legacy and current UUID in cache — must unregister the stale legacy accessory ' , ( ) => {
171+ it ( 'Scenario B: both legacy and current UUID in cache — must preserve legacy accessory and remove the newer empty duplicate ' , ( ) => {
164172 const { mockLog, mockApi, mockUpdateSensor, uuidMap } = buildMocks ( )
165173
166174 const v3Accessory = { UUID : uuidMap [ UPDATE_SENSOR_UUID_KEY ] , displayName : 'PluginUpdate' }
167175 const legacyAccessory = { UUID : uuidMap [ LEGACY_UPDATE_SENSOR_UUID_KEY ] , displayName : 'Plugin Update Check' }
168176
169177 runMigrationSimulation ( mockApi , mockLog , mockUpdateSensor , uuidMap , [ v3Accessory , legacyAccessory ] )
170178
171- // v3 accessory configured as update sensor
172- expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenCalledWith ( v3Accessory )
173- // Legacy unregistered
174- expect ( mockApi . unregisterPlatformAccessories ) . toHaveBeenCalledWith ( PLUGIN_NAME , PLATFORM_NAME , [ legacyAccessory ] )
175- // No new accessory created (v3 already registered)
179+ // configureAccessory called exactly twice: once for the v3 (during restore) then
180+ // again for the legacy during migration — legacy must be the final (last) call.
181+ expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenCalledTimes ( 2 )
182+ expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenNthCalledWith ( 1 , v3Accessory )
183+ expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenLastCalledWith ( legacyAccessory )
184+ // Current-UUID empty duplicate is removed
185+ expect ( mockApi . unregisterPlatformAccessories ) . toHaveBeenCalledWith ( PLUGIN_NAME , PLATFORM_NAME , [ v3Accessory ] )
186+ // No new accessory created
176187 expect ( mockApi . registerPlatformAccessories ) . not . toHaveBeenCalled ( )
177- expect ( mockLog . info ) . toHaveBeenCalledWith ( expect . stringContaining ( 'Removed stale legacy update sensor accessory' ) )
188+ expect ( mockLog . info ) . toHaveBeenCalledWith ( expect . stringContaining ( 'Migrated update sensor to legacy cached accessory' ) )
178189 } )
179190
180191 it ( 'Scenario B (legacy processed first): same result regardless of configureAccessory order' , ( ) => {
@@ -186,8 +197,12 @@ describe('PluginUpdatePlatform legacy UUID migration', () => {
186197 // Legacy accessory arrives first in configureAccessory order
187198 runMigrationSimulation ( mockApi , mockLog , mockUpdateSensor , uuidMap , [ legacyAccessory , v3Accessory ] )
188199
189- expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenCalledWith ( v3Accessory )
190- expect ( mockApi . unregisterPlatformAccessories ) . toHaveBeenCalledWith ( PLUGIN_NAME , PLATFORM_NAME , [ legacyAccessory ] )
200+ // Only the v3 accessory is processed during restore (legacy is held), then the
201+ // migration promotes the legacy one — so configureAccessory is still called twice
202+ // and the legacy accessory is always the last call.
203+ expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenCalledTimes ( 2 )
204+ expect ( mockUpdateSensor . configureAccessory ) . toHaveBeenLastCalledWith ( legacyAccessory )
205+ expect ( mockApi . unregisterPlatformAccessories ) . toHaveBeenCalledWith ( PLUGIN_NAME , PLATFORM_NAME , [ v3Accessory ] )
191206 expect ( mockApi . registerPlatformAccessories ) . not . toHaveBeenCalled ( )
192207 } )
193208
0 commit comments