import { NoOperationCC } from "../commandclass/NoOperationCC" ; import { GetControllerVersionRequest } from "../controller/GetControllerVersionMessages" ; import { RemoveFailedNodeRequest } from "../controller/RemoveFailedNodeMessages" ; import { SendDataRequest } from "../controller/SendDataMessages" ; import { MessagePriority } from "../message/Constants" ; import { getDefaultPriority , Message } from "../message/Message" ; import type { ZWaveNode } from "../node/Node" ; import { NodeStatus } from "../node/Types" ; import type { Driver } from "./Driver" ; import { MessageGenerator , Transaction , TransactionOptions , } from "./Transaction" ; function createDummyMessageGenerator ( msg ) { return { start : async function * ( ) { this . current = msg ; yield msg ; } , self : undefined , current : undefined } ; } function createDummyTransaction ( options ) { options . priority ??= MessagePriority . Normal ; options . message ??= { } as any ; options . parts = createDummyMessageGenerator ( options . message ! ) ; return new Transaction ( undefined as any , options as TransactionOptions ) ; } interface MockNode { id : number ; canSleep : boolean ; status : NodeStatus ; isCCSecure : ZWaveNode [ "isCCSecure" ] ; getEndpoint : ZWaveNode [ "getEndpoint" ] ; } describe ( "lib/driver/Transaction => " , ( ) => { it ( "should compare priority, then the timestamp" , ( ) => { const t1 = createDummyTransaction ( { priority : MessagePriority . Controller } ) ; const t2 = createDummyTransaction ( { priority : MessagePriority . Controller } ) ; expect ( t1 . compareTo ( t2 ) ) . toBe ( - 1 ) ; expect ( t2 . compareTo ( t1 ) ) . toBe ( 1 ) ; const t3 = createDummyTransaction ( { priority : MessagePriority . Poll } ) ; const t4 = createDummyTransaction ( { priority : MessagePriority . Controller } ) ; expect ( t3 . compareTo ( t4 ) ) . toBe ( 1 ) ; expect ( t4 . compareTo ( t3 ) ) . toBe ( - 1 ) ; const t5 = createDummyTransaction ( { priority : MessagePriority . Controller } ) ; const t6 = createDummyTransaction ( { priority : MessagePriority . Controller } ) ; t6 . creationTimestamp = t5 . creationTimestamp ; expect ( t5 . compareTo ( t6 ) ) . toBe ( 0 ) ; expect ( t6 . compareTo ( t5 ) ) . toBe ( 0 ) ; } ) ; it ( "NodeQuery comparisons should prioritize listening nodes" , ( ) => { interface MockNode { isListening : boolean ; isFrequentListening : boolean ; isCCSecure : ZWaveNode [ "isCCSecure" ] ; getEndpoint : ZWaveNode [ "getEndpoint" ] ; } const driverMock = { controller : { nodes : new Map < number , MockNode > ( [ [ 1 , { isListening : false , isFrequentListening : false , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , [ 2 , { isListening : true , isFrequentListening : false , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , [ 3 , { isListening : true , isFrequentListening : true , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , [ 4 , { isListening : false , isFrequentListening : true , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , ] ) } , getSafeCCVersionForNode ( ) { } , options : { attempts : { } } } ; function createTransactionForNode ( nodeId , priority = MessagePriority . NodeQuery ) { const driver = driverMock as any as Driver ; const msg = nodeId != undefined ? new SendDataRequest ( driver , { command : new NoOperationCC ( driver , { nodeId } ) } ) : new GetControllerVersionRequest ( driver ) ; const ret = createDummyTransaction ( { priority , message : msg } ) ; ret . creationTimestamp = 0 ; return ret ; } const tNone = createTransactionForNode ( 1 ) ; const tList = createTransactionForNode ( 2 ) ; const tFreq = createTransactionForNode ( 3 ) ; const tListFreq = createTransactionForNode ( 4 ) ; const tNoId = createTransactionForNode ( undefined as any ) ; const tNoNode = createTransactionForNode ( 5 ) ; const tListNormalPrio = createTransactionForNode ( 2 , MessagePriority . Normal ) ; const tListLowPrio = createTransactionForNode ( 2 , MessagePriority . Poll ) ; const tNoneNormalPrio = createTransactionForNode ( 1 , MessagePriority . Normal ) ; const tNoneLowPrio = createTransactionForNode ( 1 , MessagePriority . Poll ) ; expect ( tNone . compareTo ( tList ) ) . toBe ( 1 ) ; expect ( tNone . compareTo ( tList ) ) . toBe ( 1 ) ; expect ( tNone . compareTo ( tFreq ) ) . toBe ( 1 ) ; expect ( tList . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tFreq . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tListFreq . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tList . compareTo ( tFreq ) ) . toBe ( 0 ) ; expect ( tList . compareTo ( tListFreq ) ) . toBe ( 0 ) ; expect ( tFreq . compareTo ( tListFreq ) ) . toBe ( 0 ) ; expect ( tFreq . compareTo ( tList ) ) . toBe ( 0 ) ; expect ( tListFreq . compareTo ( tList ) ) . toBe ( 0 ) ; expect ( tNone . compareTo ( tListLowPrio ) ) . toBe ( 1 ) ; expect ( tNone . compareTo ( tListNormalPrio ) ) . toBe ( 1 ) ; expect ( tListLowPrio . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tListNormalPrio . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tNone . compareTo ( tNoneNormalPrio ) ) . toBe ( 1 ) ; expect ( tNone . compareTo ( tNoneLowPrio ) ) . toBe ( - 1 ) ; expect ( tNoneNormalPrio . compareTo ( tNone ) ) . toBe ( - 1 ) ; expect ( tNoneLowPrio . compareTo ( tNone ) ) . toBe ( 1 ) ; expect ( tNoId . compareTo ( tNone ) ) . toBe ( 0 ) ; expect ( tNoId . compareTo ( tList ) ) . toBe ( 0 ) ; expect ( tFreq . compareTo ( tNoId ) ) . toBe ( 0 ) ; expect ( tListFreq . compareTo ( tNoId ) ) . toBe ( 0 ) ; expect ( tNoNode . compareTo ( tNone ) ) . toBe ( 0 ) ; expect ( tNoNode . compareTo ( tList ) ) . toBe ( 0 ) ; expect ( tFreq . compareTo ( tNoNode ) ) . toBe ( 0 ) ; expect ( tListFreq . compareTo ( tNoNode ) ) . toBe ( 0 ) ; } ) ; it ( "Messages in the wakeup queue should be preferred over lesser priorities only if the node is awake" , ( ) => { const driverMock = { controller : { nodes : new Map < number , MockNode > ( [ [ 1 , { id : 1 , canSleep : false , status : NodeStatus . Alive , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , [ 2 , { id : 2 , canSleep : true , status : NodeStatus . Asleep , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , ] ) } , getSafeCCVersionForNode ( ) { } , options : { attempts : { } } } as unknown as Driver ; function createTransaction ( nodeId , priority ) { const driver = driverMock as any as Driver ; const msg = new SendDataRequest ( driver , { command : new NoOperationCC ( driver , { nodeId } ) } ) ; const ret = createDummyTransaction ( { priority , message : msg } ) ; ret . creationTimestamp = 0 ; return ret ; } const tAwakeHigh = createTransaction ( 1 , MessagePriority . Controller ) ; const tAwakeWU = createTransaction ( 1 , MessagePriority . WakeUp ) ; const tAwakeLow = createTransaction ( 1 , MessagePriority . Poll ) ; const tAsleepHigh = createTransaction ( 2 , MessagePriority . Controller ) ; const tAsleepWU = createTransaction ( 2 , MessagePriority . WakeUp ) ; const tAsleepLow = createTransaction ( 2 , MessagePriority . Poll ) ; expect ( tAwakeHigh . compareTo ( tAwakeWU ) ) . toBe ( - 1 ) ; expect ( tAwakeHigh . compareTo ( tAwakeLow ) ) . toBe ( - 1 ) ; expect ( tAwakeWU . compareTo ( tAwakeLow ) ) . toBe ( - 1 ) ; expect ( tAwakeWU . compareTo ( tAwakeHigh ) ) . toBe ( 1 ) ; expect ( tAwakeLow . compareTo ( tAwakeHigh ) ) . toBe ( 1 ) ; expect ( tAwakeLow . compareTo ( tAwakeWU ) ) . toBe ( 1 ) ; expect ( tAsleepHigh . compareTo ( tAsleepWU ) ) . toBe ( - 1 ) ; expect ( tAsleepHigh . compareTo ( tAsleepLow ) ) . toBe ( - 1 ) ; expect ( tAsleepWU . compareTo ( tAsleepLow ) ) . toBe ( - 1 ) ; expect ( tAsleepWU . compareTo ( tAsleepHigh ) ) . toBe ( 1 ) ; expect ( tAsleepLow . compareTo ( tAsleepHigh ) ) . toBe ( 1 ) ; expect ( tAsleepLow . compareTo ( tAsleepWU ) ) . toBe ( 1 ) ; expect ( tAsleepWU . compareTo ( tAwakeHigh ) ) . toBe ( 1 ) ; expect ( tAsleepWU . compareTo ( tAwakeWU ) ) . toBe ( 1 ) ; expect ( tAsleepWU . compareTo ( tAwakeLow ) ) . toBe ( 1 ) ; expect ( tAwakeHigh . compareTo ( tAsleepWU ) ) . toBe ( - 1 ) ; expect ( tAwakeWU . compareTo ( tAsleepWU ) ) . toBe ( - 1 ) ; expect ( tAwakeLow . compareTo ( tAsleepWU ) ) . toBe ( - 1 ) ; } ) ; it ( "Controller message should be preferred over messages for sleeping nodes" , ( ) => { const driverMock = { controller : { nodes : new Map < number , MockNode > ( [ [ 1 , { id : 1 , canSleep : false , status : NodeStatus . Alive , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , [ 2 , { id : 2 , canSleep : true , status : NodeStatus . Asleep , isCCSecure : ( ) => false , getEndpoint : ( ) => undefined as any } , ] , ] ) } , getSafeCCVersionForNode ( ) { } , options : { attempts : { } } } as unknown as Driver ; let creationTimestamp = 0 ; function createTransaction ( msg , priority = getDefaultPriority ( msg ) ! ) { const ret = createDummyTransaction ( { priority , message : msg } ) ; ret . creationTimestamp = ++ creationTimestamp ; return ret ; } const msgForSleepingNode = new SendDataRequest ( driverMock , { command : new NoOperationCC ( driverMock , { nodeId : 2 } ) } ) ; const tSleepingNode = createTransaction ( msgForSleepingNode , MessagePriority . WakeUp ) ; const msgForController = new RemoveFailedNodeRequest ( driverMock , { failedNodeId : 3 } ) ; const tController = createTransaction ( msgForController ) ; expect ( tController . compareTo ( tSleepingNode ) ) . toBe ( - 1 ) ; } ) ; it ( "should capture a stack trace where it was created" , ( ) => { const test = createDummyTransaction ( { message : "FOOBAR" as any } ) ; expect ( test . stack ) . toInclude ( "Transaction.test.ts" ) ; expect ( test . stack ) . not . toInclude ( "FOOBAR" ) ; } ) ; } ) ;