Skip to content

Commit

Permalink
Beta
Browse files Browse the repository at this point in the history
  • Loading branch information
NorthernMan54 committed Aug 12, 2024
1 parent 5b1272b commit 3be5efe
Show file tree
Hide file tree
Showing 12 changed files with 452 additions and 128 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ All notable changes to this project will be documented in this file. This projec

## 0.7.1 (2023-05-30)

## [Version 0.7.0](https://github.com/northernman54/homebridge-alexa/compare/v0.7.0...v0.7.1)
## [Version 0.7.1](https://github.com/northernman54/homebridge-alexa/compare/v0.7.0...v0.7.1)

#### Changes

- Update `filter` to accept a list of addresses and ports ie "192.168.1.11:51551, 192.168.1.11:46047"

## 0.7.0 (2023-05-30)

## [Version 0.7.0](https://github.com/northernman54/homebridge-alexa/compare/v0.6.9...v0.7.0)
Expand Down
2 changes: 1 addition & 1 deletion config.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
"title": "<b>Homebridge Instance Filter</b>",
"type": "string",
"placeholder": "eg. 192.168.1.122:51826",
"description": "Limits accessories shared with Alexa to a single homebridge instance. Uses ip address and port of homebridge instance.",
"description": "Limits accessories shared with Alexa to particular homebridge instances. Uses ip address and port of homebridge instance, eg. '192.168.1.122:51826' or '192.168.1.11:51551, 192.168.1.11:46047'.",
"pattern": "^[^{}/ :\\\\]+(?::\\d+)?$"
},
"deviceListHandling": {
Expand Down
2 changes: 1 addition & 1 deletion test/jest.json → jest.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "../",
"rootDir": "./",
"testEnvironment": "node",
"testRegex": ".test.ts$",
"testTimeout": 10000,
Expand Down
226 changes: 142 additions & 84 deletions package-lock.json

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,25 +34,25 @@
"devDependencies": {
"@oznu/hap-client": "^1.9.0",
"@types/jest": "^29.5.10",
"@types/node": "^20.14.10",
"@types/node": "^22.2.0",
"babel-jest": "^29.7.0",
"documentation": "^14.0.3",
"jest": "^29.7.0",
"mqtt-connection": "^4.1.0",
"nodemon": "^3.1.4",
"rimraf": "^6.0.1",
"semver": "^7.5.4",
"ts-jest": "^29.2.2",
"semver": "^7.6.3",
"ts-jest": "^29.2.4",
"ts-node": "^10.9.1",
"typescript": "^5.5.3",
"typescript": "^5.5.4",
"ws": "^8.14.2"
},
"dependencies": {
"bottleneck": "2.19.5",
"debug": "4.3.5",
"hap-node-client": "0.2.7",
"debug": "4.3.6",
"hap-node-client": "0.2.8-beta.12",
"is-my-json-valid": "2.20.6",
"mqtt": "5.8.0"
"mqtt": "5.9.1"
},
"author": "NorthernMan54",
"license": "Apache-2.0",
Expand Down
78 changes: 50 additions & 28 deletions test/client.test.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,77 @@
import { Server } from 'net';
import { MqttServer, MqttSecureServer, MqttServerListener } from './server'
import serverBuilder from './server_helpers_for_client_tests';
var AlexaLocal = require('../lib/alexaLocal.js').alexaLocal;

jest.setTimeout(30000);

// const ports = getPorts(2)
var server: MqttServer;
var alexa: any;

describe('MQTT Client', () => {
beforeAll(async () => {

var version;
let client = null
let publishCount = 0
const server2 = serverBuilder('mqtt', (serverClient) => {
var version: number;
server = serverBuilder('mqtt', (serverClient) => {
serverClient.on('connect', () => {
console.log('connect');
const connack =
version === 5 ? { reasonCode: 0 } : { returnCode: 0 }
serverClient.connack(connack)
})
serverClient.on('publish', (packet) => {
serverClient.on('publish', (packet: { payload: { toString: () => any; }; qos: number; messageId: any; }) => {
console.log('publish', packet.payload.toString());
if (packet.qos !== 0) {
serverClient.puback({ messageId: packet.messageId })
}
})
serverClient.on('auth', (packet: any) => {
console.log('auth');
if (serverClient.writable) return false
const rc = 'reasonCode'
const connack = {}
connack[rc] = 0
serverClient.connack(connack)
})
serverClient.on('subscribe', (packet: { subscriptions: any[]; messageId: any; }) => {
console.log('subscribe', packet.subscriptions);
if (!serverClient.writable) return false
serverClient.suback({
messageId: packet.messageId,
granted: packet.subscriptions.map((e: { qos: any; }) => e.qos),
})
})
})
console.log('MQTT Server starting listener')
await server.listen(1883)
console.log('MQTT Server listening')
var options = {
// MQTT Options
log: console.log,
mqttURL: 'mqtt://localhost:1883',
transport: 'mqtt',
mqttOptions: {
username: 'TEST',
password: 'PASSWORD',
reconnectPeriod: 33, // Increased reconnect period to allow DDOS protection to reset
keepalive: 55,
rejectUnauthorized: false
},
};
alexa = new AlexaLocal(options);
}, 300000);

afterAll(async () => {

// await sleep(10000)

console.log('MQTT Server exiting', server.listening)
if (server.listening) {
await server.close()
}
});

describe('Validate Inital Startup', () => {
test('getconf GNU_LIBC_VERSION', async () => {
var options = {
// MQTT Options
log: console.log,
mqttURL: 'mqtt://localhost:1883',
transport: 'mqtt',
mqttOptions: {
username: 'TEST',
password: 'PASSWORD',
reconnectPeriod: 33, // Increased reconnect period to allow DDOS protection to reset
keepalive: 55,
rejectUnauthorized: false
},

};
var alexa = new AlexaLocal(options);
await sleep(10000)
test('Discover Devices', async () => {
server.publish('command/TEST/1', {"directive":{"header":{"namespace":"Alexa.Discovery","name":"Discover","payloadVersion":"3","messageId":"e68a6720-1222-4030-b044-f01360935f18"},"payload":{}}});
expect(server).toReceiveMessage('junk');
// await sleep(10000)
});


Expand All @@ -58,6 +80,6 @@ describe('MQTT Client', () => {

});

async function sleep(ms) {
async function sleep(ms: number) {
return new Promise(resolve => setTimeout(resolve, ms));
}
166 changes: 166 additions & 0 deletions test/matchers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { diff } from "jest-diff";
import WS from "./websocket";
import { DeserializedMessage } from "./websocket";

type ReceiveMessageOptions = {
timeout?: number;
};

declare global {
namespace jest {
interface Matchers<R, T> {
toReceiveMessage<TMessage = object>(
message: DeserializedMessage<TMessage>,
options?: ReceiveMessageOptions,
): Promise<R>;
toHaveReceivedMessages<TMessage = object>(
messages: Array<DeserializedMessage<TMessage>>,
): R;
}
}
}

const WAIT_DELAY = 1000;
const TIMEOUT = Symbol("timoeut");

const makeInvalidWsMessage = function makeInvalidWsMessage(
this: jest.MatcherUtils,
ws: WS,
matcher: string,
) {
return (
this.utils.matcherHint(
this.isNot ? `.not.${matcher}` : `.${matcher}`,
"WS",
"expected",
) +
"\n\n" +
`Expected the websocket object to be a valid WS mock.\n` +
`Received: ${typeof ws}\n` +
` ${this.utils.printReceived(ws)}`
);
};

expect.extend({
async toReceiveMessage(
ws: WS,
expected: DeserializedMessage,
options?: ReceiveMessageOptions,
) {
const isWS = ws instanceof WS;
if (!isWS) {
return {
pass: !!this.isNot, // always fail
message: makeInvalidWsMessage.bind(this, ws, "toReceiveMessage"),
};
}

const waitDelay = options?.timeout ?? WAIT_DELAY;

let timeoutId;
const messageOrTimeout = await Promise.race([
ws.nextMessage,
new Promise((resolve) => {
timeoutId = setTimeout(() => resolve(TIMEOUT), waitDelay);
}),
]);
clearTimeout(timeoutId);

if (messageOrTimeout === TIMEOUT) {
return {
pass: !!this.isNot, // always fail
message: () =>
this.utils.matcherHint(
this.isNot ? ".not.toReceiveMessage" : ".toReceiveMessage",
"WS",
"expected",
) +
"\n\n" +
`Expected the websocket server to receive a message,\n` +
`but it didn't receive anything in ${waitDelay}ms.`,
};
}
const received = messageOrTimeout;

const pass = this.equals(received, expected);

const message = pass
? () =>
this.utils.matcherHint(".not.toReceiveMessage", "WS", "expected") +
"\n\n" +
`Expected the next received message to not equal:\n` +
` ${this.utils.printExpected(expected)}\n` +
`Received:\n` +
` ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, { expand: this.expand });
return (
this.utils.matcherHint(".toReceiveMessage", "WS", "expected") +
"\n\n" +
`Expected the next received message to equal:\n` +
` ${this.utils.printExpected(expected)}\n` +
`Received:\n` +
` ${this.utils.printReceived(received)}\n\n` +
`Difference:\n\n${diffString}`
);
};

return {
actual: received,
expected,
message,
name: "toReceiveMessage",
pass,
};
},

toHaveReceivedMessages(ws: WS, messages: Array<DeserializedMessage>) {
const isWS = ws instanceof WS;
if (!isWS) {
return {
pass: !!this.isNot, // always fail
message: makeInvalidWsMessage.bind(this, ws, "toHaveReceivedMessages"),
};
}

const received = messages.map((expected) =>
// object comparison to handle JSON protocols
ws.messages.some((actual) => this.equals(actual, expected)),
);
const pass = this.isNot ? received.some(Boolean) : received.every(Boolean);
const message = pass
? () =>
this.utils.matcherHint(
".not.toHaveReceivedMessages",
"WS",
"expected",
) +
"\n\n" +
`Expected the WS server to not have received the following messages:\n` +
` ${this.utils.printExpected(messages)}\n` +
`But it received:\n` +
` ${this.utils.printReceived(ws.messages)}`
: () => {
return (
this.utils.matcherHint(
".toHaveReceivedMessages",
"WS",
"expected",
) +
"\n\n" +
`Expected the WS server to have received the following messages:\n` +
` ${this.utils.printExpected(messages)}\n` +
`Received:\n` +
` ${this.utils.printReceived(ws.messages)}\n\n`
);
};

return {
actual: ws.messages,
expected: messages,
message,
name: "toHaveReceivedMessages",
pass,
};
},
});
4 changes: 2 additions & 2 deletions test/server.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as net from 'net'
import { TlsOptions } from 'tls'
import * as tls from 'tls'
import Connection from 'mqtt-connection'
import * as Connection from 'mqtt-connection'
import { Duplex } from 'stream'

export type MqttServerListener = (client: Connection) => void
Expand Down Expand Up @@ -29,7 +29,7 @@ export class MqttServer extends net.Server {
this.on('client', listener)
}
}
}
}

/**
* MqttServerNoWait (w/o waiting for initialization)
Expand Down
7 changes: 7 additions & 0 deletions test/server_helpers_for_client_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,21 @@ export default function serverBuilder(
handler?: MqttServerListener,
): Server {
const sockets = []


const defaultHandler: MqttServerListener = (serverClient) => {

sockets.push(serverClient)

serverClient.on('auth', (packet) => {
console.log('auth');
if (serverClient.writable) return false
const rc = 'reasonCode'
const connack = {}
connack[rc] = 0
serverClient.connack(connack)
})

serverClient.on('connect', (packet) => {
if (!serverClient.writable) return false
let rc = 'returnCode'
Expand Down Expand Up @@ -63,6 +69,7 @@ export default function serverBuilder(
})

serverClient.on('publish', (packet) => {
console.log('publish');
if (!serverClient.writable) return false
setImmediate(() => {
switch (packet.qos) {
Expand Down
Loading

0 comments on commit 3be5efe

Please sign in to comment.