Files
de.captica.nodered.node.sys…/test/systacomfort2-spec.js

155 lines
9.0 KiB
JavaScript

/*
* Test implementation of systacomfort2 node
*
* Project: de.captica.nodered.node.systacomfort2
* Author: Marc Böhm <marc.boehm@captica.de>
* Copyright (c) captica GmbH est. 2022 / MIT License
*/
const { expect } = require('chai')
const dgram = require('node:dgram')
const systaComfort2Node = require('../systacomfort2/systacomfort2')
const { dataParser, SYSTACOMFORT_VALUES } = require('../systacomfort2/dataparser')
const { assert } = require('node:console')
const MESSAGE_INIT = 'AJe+LI4NzW0JCQwAMtoAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWAIAAAEAAAACAAAAAAAAADIAAAAyAAAAMgAAAGQAAAC4MP4/APgBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABqAwAAGgMAAE4DAAA8AwAAUgEAAAAAAAA5AAAAlwMAAA0AAAAAAAAAvAIAAIQDAAC8AgAAvAAAAC6TBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAy9tHAsmsAQBQAAAANwAAAAAAAAAEAAAAGgMAAGsDAABOAwAAUgEAAAAAAABrAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAruTDkA=='
const MESSAGE_INIT_ANSWER = 'AJe+LI4NzW0AAAAAAQAAABGcjK0='
const MESSAGE_DATA = 'AJe+LI4NzG0JCQwAMtoAAAEAAAAAAAAAOgEAAFYBAADU/v//SwMAAEUDAABCAwAA1P7//9T+///U/v//AAAAAAAAAABrAwAAXwIAAFoCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFgCAADNAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAyAAAAOYAAACgAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAA+gAAAAAAAAAGAAAAAAAAACYCAADIAAAAZAAAABQAAAB4AAAAAAAAADIAAABQAAAAZAAAAAUAAAACAAAAMgAAAB4AAAAAAAAAAAAAAAAAAAADAAAAMgAAAPQBAAAEAAAAMgAAAHISAADYAQAAAAAAAAAAAAABAAAAyAAAANwAAACWAAAAAAAAAAAAAAAAAAAAIwcAACQHAAAAAAAAXgEAAAAAAAANAAAAAAAAALwCAADIAAAAZAAAABQAAAB4AAAAAAAAAAAAAADIAAAAZAAAAAUAAAACAAAAMgAAAB4AAAAAAAAAAAAAAAAAAAADAAAAMgAAAPQBAAAEAAAAMgAAACMHAAAAAAAAAAAAAAAAAAABAAAAyAAAANwAAACWAAAAAAAAAAAAAAAAAAAAIwcAACQHAABeAQAAAAAAAA0AAAAAAAAAvAIAAMgAAABkAAAAFAAAAHgAAAAAAAAAAAAAAMgAAABkAAAABQAAAAIAAAAAAAAAAAAAAAMAAAAyAAAA9AEAAAQAAAAyAAAAIwcAAAAAAABYAgAAigIAAAEAAABkAAAAZAAAAAAAAAAyAAAAUgMAAAAAAAAFAAAAtgMAAAAAAAAyAAAAAAAAAJABAABkAAAAAQAAAAEAAAADAAAAAwAAAAMAAAAPAAAAMgAAAKQAAAAAAAAAAAAAABMAAAAFAAAAAAAAAAAAAAAjHAAAIwAAAAVZAAANAAAAEgAAAB51AAAcAAAAAAAAAAAAAAAAAAAALAEAAIoCAABkAAAAGQAAAAAAAAAAAAAA+gAAABgBAAAAAAAAAAAAAAAAAAC8AgAAAAAAAMgAAABkAAAAAgAAAMgAAAAeAAAAAAAAABQAAAAeAAAAHgAAADIAAAAeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/wAAAAAAAAAAAAAAAQAAAAEAAAAHAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/h54Ug=='
const TOPIC_PREFIX = 'my/custom/topic/prefix'
const CONFIG = { listenPort: 44556 }
const OUTPUT_MESSAGE_PAYLOAD = {
BOILER_START_COUNTER: 22789,
BOILER_WORKING_HOURS: 7203,
BUFFER_TANK_TEMPERATURE_TOP: 83.7,
BUFFER_TANK_TEMPERATURE_BOTTOM: 83.4,
SOLAR_YIELD_CURRENT: 1.3,
SOLAR_YIELD_DAY: 18,
SOLAR_YIELD_TOTAL: 29982,
SOLAR_TEMPERATURE_COLLECTOR:87.5,
SYSTEM_TEMPERATURE_FLOW: 34.2,
SYSTEM_TEMPERATURE_FLOW_RETURN: null,
SYSTEM_TEMPERATURE_EXTERNAL: 31.4,
SYSTEM_TEMPERATURE_PROCESS_WATER_CURRENT: 84.3,
SYSTEM_TEMPERATURE_PROCESS_WATER_TARGET: 60,
SYSTEM_TEMPERATURE_ROOM_TARGET: 20.5,
}
/**
* Node-RED registration/handling
*/
describe('SystaComfort2 - Node-RED node', () => {
// Constructor will be announced on register. We have to keep the object for later tests
let testNodeConstructor = null
let closeCallback = null
// Check node registration first. We fake the RED-objec to capture the registration part
systaComfort2Node({
nodes: {
registerType: function(name, nodeConstructor) {
it('should be registered on node-RED as a new node type', () => {
expect(name).to.equal('systacomfort2')
expect(nodeConstructor).to.not.be.undefined
})
testNodeConstructor = nodeConstructor
},
createNode: function(context, config) {
// Extend context with additional special function (normally provided by node red context object)
context.on = function(eventName, callback) {
closeCallback = callback
}
context.status = function(statusObj) {
it('status object should be present', () => {
expect(configObj).to.not.be.undefined
})
}
it('should be provided the same config object', () => {
expect(config).to.be.equal(CONFIG)
})
}
}
})
/**
* Call the node constructor
*/
it('should be possible to call node constructor and access udp server', () => {
try {
testNodeConstructor(CONFIG)
// Create a udp client to connect to service
const client = dgram.createSocket('udp4')
client.connect(CONFIG.listenPort, (err) => {
expect(err).to.be.undefined
client.close()
})
}
finally {
// Don't forget to send close event after tests
closeCallback()
}
})
})
/**
* Data parser
*/
describe('SystaComfort2 - Data Parser', function(done) {
/**
* MAC address utils
*/
describe('#createMACPart()', () => {
it('should convert a byte into string and pad with a leading "0"', () => {
expect(dataParser.createMACPart(15)).to.equal('0f')
})
it('should convert a byte into as string and NOT pad with a leading "0"', () => {
expect(dataParser.createMACPart(255)).to.equal('ff')
})
})
/**
* Check init answer conversion
*/
describe('#createResponse()', () => {
it('should create an response data buffer based on the input message', () => {
let answerBuffer = dataParser.createResponse(Buffer.from(MESSAGE_INIT, 'base64'))
expect(answerBuffer).to.not.be.undefined
expect(answerBuffer.toString('base64')).to.equal(MESSAGE_INIT_ANSWER)
})
})
/**
* Check data parser
*/
describe('#parseData()', () => {
it('should parse the data message and return an null object because of invalid packet type != 1', () => {
let outputMsg = dataParser.parseData(Buffer.from(MESSAGE_INIT, 'base64'))
expect(outputMsg).to.be.null
})
it('should parse the data message and provide a structured output message', () => {
let outputMsg = dataParser.parseData(Buffer.from(MESSAGE_DATA, 'base64'))
expect(outputMsg).to.not.be.undefined
})
it('should parse the data message and return an structured output message where payload topic starts with: ' + TOPIC_PREFIX, () => {
let outputMsg = dataParser.parseData(Buffer.from(MESSAGE_DATA, 'base64'), TOPIC_PREFIX)
expect(outputMsg).to.not.be.undefined
expect(outputMsg).to.have.own.property('payload')
expect(outputMsg.payload).to.not.be.undefined
Object.values(outputMsg.payload).forEach(valueObj => {
expect(valueObj).to.not.be.undefined
expect(valueObj).to.have.own.property('topic')
expect(valueObj.topic).to.not.be.null
expect(valueObj.topic.startsWith(TOPIC_PREFIX)).to.be.true
})
})
/**
* Check values
*/
let outputMsg = dataParser.parseData(Buffer.from(MESSAGE_DATA, 'base64'))
Object.entries(SYSTACOMFORT_VALUES).forEach(([key, valueObj]) => {
const expectedValue = OUTPUT_MESSAGE_PAYLOAD[key]
it(`should have the value ${key} in payload object and with value ${expectedValue}, description, topic and unit`, () => {
expect(outputMsg.payload).to.have.own.property(key)
let payloadValue = outputMsg.payload[key]
expect(payloadValue).to.not.be.undefined
expect(payloadValue.value).to.be.equal(expectedValue)
expect(payloadValue.topic).to.be.equal(valueObj.topic)
expect(payloadValue.description).to.be.equal(valueObj.description)
expect(payloadValue.unit).to.be.equal(valueObj.unit)
})
})
})
})