import { assertZWaveError , ZWaveErrorCodes } from "@zwave-js/core" ; import type { Driver } from "../driver/Driver" ; import type { INodeQuery } from "../node/INodeQuery" ; import { createEmptyMockDriver } from "../test/mocks" ; import { FunctionType , MessageType } from "./Constants" ; import { Message , messageTypes } from "./Message" ; const fakeDriver = createEmptyMockDriver ( ) as unknown as Driver ; describe ( "lib/message" , ( ) => { describe ( "Message" , ( ) => { it ( "should deserialize and serialize correctly" , ( ) => { const okayMessages = [ Buffer . from ( [ 0x01 , 0x09 , 0x00 , 0x13 , 0x03 , 0x02 , 0x00 , 0x00 , 0x25 , 0x0b , 0xca , ] ) , Buffer . from ( [ 0x01 , 0x05 , 0x00 , 0x47 , 0x04 , 0x20 , 0x99 ] ) , Buffer . from ( [ 0x01 , 0x06 , 0x00 , 0x46 , 0x0c , 0x0d , 0x32 , 0x8c ] ) , Buffer . from ( [ 0x01 , 0x0a , 0x00 , 0x13 , 0x03 , 0x03 , 0x8e , 0x02 , 0x04 , 0x25 , 0x40 , 0x0b , ] ) , ] ; for ( const original of okayMessages ) { const parsed = new Message ( fakeDriver , { data : original } ) ; expect ( parsed . serialize ( ) ) . toEqual ( original ) ; } } ) ; it ( "should serialize correctly when the payload is null" , ( ) => { const expected = Buffer . from ( [ 0x01 , 0x03 , 0x00 , 0xff , 0x03 ] ) ; const message = new Message ( fakeDriver , { type : MessageType . Request , functionType : 0xff , } ) ; expect ( message . serialize ( ) ) . toEqual ( expected ) ; } ) ; it ( "should throw the correct error when parsing a faulty message" , ( ) => { const brokenMessages = [ [ Buffer . from ( [ 0x01 , 0x02 , 0x00 , 0x00 ] ) , "truncated" , ZWaveErrorCodes . PacketFormat_Truncated , ] , [ Buffer . from ( [ 0x00 , 0x03 , 0x00 , 0x00 , 0x00 ] ) , "start with SOF" , ZWaveErrorCodes . PacketFormat_Invalid , ] , [ Buffer . from ( [ 0x01 , 0x04 , 0x00 , 0x00 , 0x00 ] ) , "truncated" , ZWaveErrorCodes . PacketFormat_Truncated , ] , [ Buffer . from ( [ 0x01 , 0x03 , 0x00 , 0x00 , 0x00 ] ) , "checksum" , ZWaveErrorCodes . PacketFormat_Checksum , ] , [ Buffer . from ( [ 0x01 , 0x05 , 0x00 , 0x47 , 0x04 , 0x20 , 0x98 ] ) , "checksum" , ZWaveErrorCodes . PacketFormat_Checksum , ] , ] ; for ( const [ message , msg , code ] of brokenMessages ) { assertZWaveError ( ( ) => new Message ( fakeDriver , { data : message } ) , { messageMatches : msg , errorCode : code , } ) ; } } ) ; it ( "isComplete() should work correctly" , ( ) => { const okayMessages = [ Buffer . from ( [ 0x01 , 0x09 , 0x00 , 0x13 , 0x03 , 0x02 , 0x00 , 0x00 , 0x25 , 0x0b , 0xca , ] ) , Buffer . from ( [ 0x01 , 0x05 , 0x00 , 0x47 , 0x04 , 0x20 , 0x99 ] ) , Buffer . from ( [ 0x01 , 0x06 , 0x00 , 0x46 , 0x0c , 0x0d , 0x32 , 0x8c ] ) , Buffer . from ( [ 0x01 , 0x0a , 0x00 , 0x13 , 0x03 , 0x03 , 0x8e , 0x02 , 0x04 , 0x25 , 0x40 , 0x0b , ] ) , ] ; for ( const msg of okayMessages ) { expect ( Message . isComplete ( msg ) ) . toBe ( true ) ; } const truncatedMessages = [ undefined , Buffer . from ( [ ] ) , Buffer . from ( [ 0x01 ] ) , Buffer . from ( [ 0x01 , 0x09 ] ) , Buffer . from ( [ 0x01 , 0x09 , 0x00 ] ) , Buffer . from ( [ 0x01 , 0x09 , 0x00 , 0x13 , 0x03 , 0x02 , 0x00 , 0x00 , 0x25 , 0x0b , ] ) , ] ; for ( const msg of truncatedMessages ) { expect ( Message . isComplete ( msg ) ) . toBe ( false ) ; } const faultyMessages = [ Buffer . from ( [ 0x01 , 0x09 , 0x00 , 0x13 , 0x03 , 0x02 , 0x00 , 0x00 , 0x25 , 0x0b , 0xca , ] ) , Buffer . from ( [ 0x01 , 0x05 , 0x00 , 0x47 , 0x04 , 0x20 , 0x99 ] ) , Buffer . from ( [ 0x01 , 0x06 , 0x00 , 0x46 , 0x0c , 0x0d , 0x32 , 0x8c ] ) , Buffer . from ( [ 0x01 , 0x0a , 0x00 , 0x13 , 0x03 , 0x03 , 0x8e , 0x02 , 0x04 , 0x25 , 0x40 , 0x0b , ] ) , ] ; for ( const msg of faultyMessages ) { expect ( Message . isComplete ( msg ) ) . toBe ( true ) ; } const tooLongMessages = [ Buffer . from ( [ 0x01 , 0x09 , 0x00 , 0x13 , 0x03 , 0x02 , 0x00 , 0x00 , 0x25 , 0x0b , 0xca , 0x00 , ] ) , Buffer . from ( [ 0x01 , 0x05 , 0x00 , 0x47 , 0x04 , 0x20 , 0x99 , 0x01 , 0x02 , ] ) , Buffer . from ( [ 0x01 , 0x06 , 0x00 , 0x46 , 0x0c , 0x0d , 0x32 , 0x8c , 0xab , 0xcd , 0xef , ] ) , Buffer . from ( [ 0x01 , 0x0a , 0x00 , 0x13 , 0x03 , 0x03 , 0x8e , 0x02 , 0x04 , 0x25 , 0x40 , 0x0b , 0x12 , ] ) , ] ; for ( const msg of tooLongMessages ) { expect ( Message . isComplete ( msg ) ) . toBe ( true ) ; } } ) ; it ( "toJSON() should return a semi-readable JSON representation" , ( ) => { const msg1 = new Message ( fakeDriver , { type : MessageType . Request , functionType : FunctionType . GetControllerVersion , } ) ; const json1 = { name : msg1 . constructor . name , type : "Request" , functionType : "GetControllerVersion" , payload : "" , } ; const msg2 = new Message ( fakeDriver , { type : MessageType . Request , functionType : FunctionType . GetControllerVersion , payload : Buffer . from ( "aabbcc" , "hex" ) , } ) ; const json2 = { name : msg2 . constructor . name , type : "Request" , functionType : "GetControllerVersion" , payload : "aabbcc" , } ; const msg3 = new Message ( fakeDriver , { type : MessageType . Response , functionType : FunctionType . GetControllerVersion , expectedResponse : FunctionType . GetControllerVersion , } ) ; const json3 = { name : msg3 . constructor . name , type : "Response" , functionType : "GetControllerVersion" , expectedResponse : "GetControllerVersion" , payload : "" , } ; const msg4 = new Message ( fakeDriver , { type : MessageType . Request , functionType : FunctionType . GetControllerVersion , expectedResponse : FunctionType . GetControllerVersion , payload : Buffer . from ( "aabbcc" , "hex" ) , } ) ; const json4 = { name : msg4 . constructor . name , type : "Request" , functionType : "GetControllerVersion" , expectedResponse : "GetControllerVersion" , payload : "aabbcc" , } ; expect ( msg1 . toJSON ( ) ) . toEqual ( json1 ) ; expect ( msg2 . toJSON ( ) ) . toEqual ( json2 ) ; expect ( msg3 . toJSON ( ) ) . toEqual ( json3 ) ; expect ( msg4 . toJSON ( ) ) . toEqual ( json4 ) ; } ) ; it ( "getConstructor() should return `Message` for an unknown packet type" , ( ) => { const unknown = Buffer . from ( [ 0x01 , 0x03 , 0x00 , 0x00 , 0xfc ] ) ; expect ( Message . getConstructor ( unknown ) ) . toBe ( Message ) ; } ) ; it ( `the constructor should throw when no message type is specified` , ( ) => { assertZWaveError ( ( ) => new Message ( fakeDriver , { functionType : 0xff } ) , { errorCode : ZWaveErrorCodes . Argument_Invalid , messageMatches : /message type/i , } ) ; @ messageTypes ( undefined as any , 0xff ) class FakeMessageWithoutMessageType extends Message { } assertZWaveError ( ( ) => new FakeMessageWithoutMessageType ( fakeDriver ) , { errorCode : ZWaveErrorCodes . Argument_Invalid , messageMatches : /message type/i , } ) ; } ) ; it ( `the constructor should throw when no function type is specified` , ( ) => { assertZWaveError ( ( ) => new Message ( fakeDriver , { type : MessageType . Request } ) , { errorCode : ZWaveErrorCodes . Argument_Invalid , messageMatches : /function type/i , } ) ; @ messageTypes ( MessageType . Request , undefined as any ) class FakeMessageWithoutFunctionType extends Message { } assertZWaveError ( ( ) => new FakeMessageWithoutFunctionType ( fakeDriver ) , { errorCode : ZWaveErrorCodes . Argument_Invalid , messageMatches : /function type/i , } ) ; } ) ; describe ( "getNodeUnsafe()" , ( ) => { it ( "returns undefined when the controller is not initialized yet" , ( ) => { const fakeDriver = { getSafeCCVersionForNode ( ) { } , } as any as Driver ; const msg = new Message ( fakeDriver , { type : MessageType . Request , functionType : 0xff , } ) ; expect ( msg . getNodeUnsafe ( ) ) . toBeUndefined ( ) ; } ) ; it ( "returns undefined when the message is no node query" , ( ) => { const fakeDriver = { getSafeCCVersionForNode ( ) { } , controller : { nodes : new Map ( ) } , } as any as Driver ; const msg = new Message ( fakeDriver , { type : MessageType . Request , functionType : 0xff , } ) ; expect ( msg . getNodeUnsafe ( ) ) . toBeUndefined ( ) ; } ) ; it ( "returns the associated node otherwise" , ( ) => { const fakeDriver = { getSafeCCVersionForNode ( ) { } , controller : { nodes : new Map ( ) } , } as any as Driver ; fakeDriver . controller . nodes . set ( 1 , { } as any ) ; const msg = new Message ( fakeDriver , { type : MessageType . Request , functionType : 0xff , } ) ; ( msg as any as INodeQuery ) . nodeId = 1 ; expect ( msg . getNodeUnsafe ( ) ) . toBe ( fakeDriver . controller . nodes . get ( 1 ) ) ; ( msg as any as INodeQuery ) . nodeId = 2 ; expect ( msg . getNodeUnsafe ( ) ) . toBeUndefined ( ) ; } ) ; } ) ; } ) ; } ) ;