@@ -2615,6 +2615,51 @@ describe("Controller", () => {
26152615 expect ( events . message . length ) . toBe ( 0 ) ;
26162616 } ) ;
26172617
2618+ it ( "Receive zclData with device deleted during processing (prevent race condition)" , async ( ) => {
2619+ const frame = Zcl . Frame . create (
2620+ Zcl . FrameType . GLOBAL ,
2621+ Zcl . Direction . CLIENT_TO_SERVER ,
2622+ true ,
2623+ undefined ,
2624+ 40 ,
2625+ "read" ,
2626+ "genTime" ,
2627+ [ { attrId : 0 } , { attrId : 1 } , { attrId : 2 } , { attrId : 3 } , { attrId : 4 } , { attrId : 5 } , { attrId : 6 } , { attrId : 7 } , { attrId : 8 } , { attrId : 9 } ] ,
2628+ { } ,
2629+ ) ;
2630+ await controller . start ( ) ;
2631+ await mockAdapterEvents . deviceJoined ( { networkAddress : 129 , ieeeAddr : "0x129" } ) ;
2632+ const device = controller . getDeviceByIeeeAddr ( "0x129" ) ! ;
2633+ const endpoint = device . getEndpoint ( 1 ) ! ;
2634+ const deviceOnZclDataSpy = vi . spyOn ( device , "onZclData" ) ;
2635+ const endpointCommandSpy = vi . spyOn ( endpoint , "command" ) ;
2636+ const endpointReadResponseSpy = vi . spyOn ( endpoint , "readResponse" ) ;
2637+ const endpointReadSpy = vi . spyOn ( endpoint , "read" ) ;
2638+ const endpointDefaultResponseSpy = vi . spyOn ( endpoint , "defaultResponse" ) ;
2639+
2640+ deviceOnZclDataSpy . mockImplementationOnce ( async ( a , b , c ) => {
2641+ await mockAdapterEvents . deviceLeave ( { networkAddress : 129 , ieeeAddr : undefined } ) ;
2642+ await device . onZclData ( a , b , c ) ;
2643+ } ) ;
2644+
2645+ await mockAdapterEvents . zclPayload ( {
2646+ wasBroadcast : false ,
2647+ address : "0x129" ,
2648+ clusterID : frame . cluster . ID ,
2649+ data : frame . toBuffer ( ) ,
2650+ header : frame . header ,
2651+ endpoint : 1 ,
2652+ linkquality : 50 ,
2653+ groupID : 1 ,
2654+ } ) ;
2655+
2656+ expect ( deviceOnZclDataSpy ) . toHaveBeenCalledTimes ( 2 ) ; // once for wrapper mock, once for real
2657+ expect ( endpointCommandSpy ) . toHaveBeenCalledTimes ( 0 ) ;
2658+ expect ( endpointReadResponseSpy ) . toHaveBeenCalledTimes ( 0 ) ;
2659+ expect ( endpointReadSpy ) . toHaveBeenCalledTimes ( 0 ) ;
2660+ expect ( endpointDefaultResponseSpy ) . toHaveBeenCalledTimes ( 0 ) ;
2661+ } ) ;
2662+
26182663 it ( "Receive readResponse from unknown endpoint" , async ( ) => {
26192664 const buffer = Buffer . from ( [ 8 , 1 , 1 , 1 , 0 , 0 , 32 , 3 ] ) ;
26202665 const frame = Zcl . Frame . fromBuffer ( Zcl . Utils . getCluster ( "genBasic" , undefined , { } ) . ID , Zcl . Header . fromBuffer ( buffer ) , buffer , { } ) ;
@@ -9252,6 +9297,52 @@ describe("Controller", () => {
92529297 expect ( events . deviceNetworkAddressChanged . length ) . toBe ( 0 ) ;
92539298 } ) ;
92549299
9300+ it ( "does not try to identify unknown device when deleted" , async ( ) => {
9301+ const nwkAddr = 40369 ;
9302+ const database = `
9303+ {"id":1,"type":"Coordinator","ieeeAddr":"0x0000012300000000","nwkAddr":0,"manufId":0,"epList":[11,6,5,4,3,2,1],"endpoints":{"1":{"profId":260,"epId":1,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"2":{"profId":257,"epId":2,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"3":{"profId":261,"epId":3,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"4":{"profId":263,"epId":4,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"5":{"profId":264,"epId":5,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"6":{"profId":265,"epId":6,"devId":5,"inClusterList":[],"meta":{},"outClusterList":[],"clusters":{}},"11":{"profId":260,"epId":11,"devId":1024,"inClusterList":[],"meta":{},"outClusterList":[1280],"clusters":{}}},"interviewCompleted":false,"meta":{},"_id":"aM341ldunExFmJ3u"}
9304+ {"id":4,"type":"EndDevice","ieeeAddr":"0x0017880104e45517","lastSeen":123,"nwkAddr":${ nwkAddr } ,"manufId":4107,"manufName":"Philips","powerSource":"Battery","modelId":"RWL021","epList":[1,2],"endpoints":{"1":{"profId":49246,"epId":1,"devId":2096,"inClusterList":[0],"meta":{},"outClusterList":[0,3,4,6,8,5],"binds":[{"type":"endpoint","endpointID":1,"deviceIeeeAddr":"0x000b57fffec6a5b2"}],"configuredReportings":[{"cluster":1,"attrId":0,"minRepIntval":1,"maxRepIntval":20,"repChange":2}],"clusters":{"genBasic":{"dir":{"value":3},"attrs":{"modelId":"RWL021"}}}},"2":{"profId":260,"epId":2,"devId":12,"inClusterList":[0,1,3,15,64512],"meta":{},"outClusterList":[25],"clusters":{}}},"appVersion":2,"stackVersion":1,"hwVersion":1,"dateCode":"20160302","swBuildId":"5.45.1.17846","zclVersion":1,"interviewState":"SUCCESSFUL","meta":{"configured":1},"_id":"qxhymbX6H2GXDw8Z"}
9305+ ` ;
9306+ fs . writeFileSync ( options . databasePath , database ) ;
9307+ await controller . start ( ) ;
9308+ events . lastSeenChanged = [ ] ;
9309+ events . deviceNetworkAddressChanged = [ ] ;
9310+ const identifyUnknownDeviceSpy = vi . spyOn ( controller , "identifyUnknownDevice" ) ;
9311+
9312+ const device = controller . getDeviceByIeeeAddr ( "0x0017880104e45517" ) ! ;
9313+ expect ( device . networkAddress ) . toStrictEqual ( nwkAddr ) ;
9314+
9315+ device . removeFromDatabase ( ) ;
9316+ expect ( device . isDeleted ) . toStrictEqual ( true ) ;
9317+ expect ( Device . isDeletedByIeeeAddr ( "0x0017880104e45517" ) ) . toStrictEqual ( true ) ;
9318+ expect ( Device . isDeletedByNetworkAddress ( nwkAddr ) ) . toStrictEqual ( true ) ;
9319+
9320+ const frame2 = Zcl . Frame . create (
9321+ 0 ,
9322+ 1 ,
9323+ true ,
9324+ undefined ,
9325+ 10 ,
9326+ "readRsp" ,
9327+ 0 ,
9328+ [ { attrId : 5 , status : 0 , dataType : 66 , attrData : "new.model.id2" } ] ,
9329+ { } ,
9330+ ) ;
9331+ await mockAdapterEvents . zclPayload ( {
9332+ wasBroadcast : false ,
9333+ address : nwkAddr ,
9334+ clusterID : frame2 . cluster . ID ,
9335+ data : frame2 . toBuffer ( ) ,
9336+ header : frame2 . header ,
9337+ endpoint : 1 ,
9338+ linkquality : 50 ,
9339+ groupID : 1 ,
9340+ } ) ;
9341+
9342+ expect ( identifyUnknownDeviceSpy ) . toHaveBeenCalledTimes ( 0 ) ;
9343+ expect ( mockLogger . debug ) . toHaveBeenCalledWith ( `Data is from unknown device with address '${ nwkAddr } ', skipping...` , "zh:controller" ) ;
9344+ } ) ;
9345+
92559346 it ( "reads/writes to group with custom cluster when common to all members" , async ( ) => {
92569347 interface CustomManuHerdsman {
92579348 attributes : { customAttr : number } ;
0 commit comments