matterbridge 3.4.1-dev-20251130-cfb291e → 3.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -9
- package/README-SERVICE-OPT.md +1 -0
- package/dist/broadcastServer.d.ts +136 -0
- package/dist/broadcastServer.d.ts.map +1 -0
- package/dist/broadcastServer.js +112 -1
- package/dist/broadcastServer.js.map +1 -0
- package/dist/broadcastServerTypes.d.ts +841 -0
- package/dist/broadcastServerTypes.d.ts.map +1 -0
- package/dist/broadcastServerTypes.js +24 -0
- package/dist/broadcastServerTypes.js.map +1 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +97 -1
- package/dist/cli.js.map +1 -0
- package/dist/cliEmitter.d.ts +50 -0
- package/dist/cliEmitter.d.ts.map +1 -0
- package/dist/cliEmitter.js +37 -0
- package/dist/cliEmitter.js.map +1 -0
- package/dist/cliHistory.d.ts +48 -0
- package/dist/cliHistory.d.ts.map +1 -0
- package/dist/cliHistory.js +38 -0
- package/dist/cliHistory.js.map +1 -0
- package/dist/clusters/export.d.ts +2 -0
- package/dist/clusters/export.d.ts.map +1 -0
- package/dist/clusters/export.js +2 -0
- package/dist/clusters/export.js.map +1 -0
- package/dist/deviceManager.d.ts +135 -0
- package/dist/deviceManager.d.ts.map +1 -0
- package/dist/deviceManager.js +113 -1
- package/dist/deviceManager.js.map +1 -0
- package/dist/devices/airConditioner.d.ts +98 -0
- package/dist/devices/airConditioner.d.ts.map +1 -0
- package/dist/devices/airConditioner.js +57 -0
- package/dist/devices/airConditioner.js.map +1 -0
- package/dist/devices/batteryStorage.d.ts +48 -0
- package/dist/devices/batteryStorage.d.ts.map +1 -0
- package/dist/devices/batteryStorage.js +48 -1
- package/dist/devices/batteryStorage.js.map +1 -0
- package/dist/devices/cooktop.d.ts +61 -0
- package/dist/devices/cooktop.d.ts.map +1 -0
- package/dist/devices/cooktop.js +56 -0
- package/dist/devices/cooktop.js.map +1 -0
- package/dist/devices/dishwasher.d.ts +71 -0
- package/dist/devices/dishwasher.d.ts.map +1 -0
- package/dist/devices/dishwasher.js +57 -0
- package/dist/devices/dishwasher.js.map +1 -0
- package/dist/devices/evse.d.ts +76 -0
- package/dist/devices/evse.d.ts.map +1 -0
- package/dist/devices/evse.js +74 -10
- package/dist/devices/evse.js.map +1 -0
- package/dist/devices/export.d.ts +17 -0
- package/dist/devices/export.d.ts.map +1 -0
- package/dist/devices/export.js +5 -0
- package/dist/devices/export.js.map +1 -0
- package/dist/devices/extractorHood.d.ts +46 -0
- package/dist/devices/extractorHood.d.ts.map +1 -0
- package/dist/devices/extractorHood.js +43 -0
- package/dist/devices/extractorHood.js.map +1 -0
- package/dist/devices/heatPump.d.ts +47 -0
- package/dist/devices/heatPump.d.ts.map +1 -0
- package/dist/devices/heatPump.js +50 -2
- package/dist/devices/heatPump.js.map +1 -0
- package/dist/devices/laundryDryer.d.ts +67 -0
- package/dist/devices/laundryDryer.d.ts.map +1 -0
- package/dist/devices/laundryDryer.js +62 -3
- package/dist/devices/laundryDryer.js.map +1 -0
- package/dist/devices/laundryWasher.d.ts +81 -0
- package/dist/devices/laundryWasher.d.ts.map +1 -0
- package/dist/devices/laundryWasher.js +70 -4
- package/dist/devices/laundryWasher.js.map +1 -0
- package/dist/devices/microwaveOven.d.ts +168 -0
- package/dist/devices/microwaveOven.d.ts.map +1 -0
- package/dist/devices/microwaveOven.js +88 -5
- package/dist/devices/microwaveOven.js.map +1 -0
- package/dist/devices/oven.d.ts +105 -0
- package/dist/devices/oven.d.ts.map +1 -0
- package/dist/devices/oven.js +85 -0
- package/dist/devices/oven.js.map +1 -0
- package/dist/devices/refrigerator.d.ts +118 -0
- package/dist/devices/refrigerator.d.ts.map +1 -0
- package/dist/devices/refrigerator.js +102 -0
- package/dist/devices/refrigerator.js.map +1 -0
- package/dist/devices/roboticVacuumCleaner.d.ts +112 -0
- package/dist/devices/roboticVacuumCleaner.d.ts.map +1 -0
- package/dist/devices/roboticVacuumCleaner.js +100 -9
- package/dist/devices/roboticVacuumCleaner.js.map +1 -0
- package/dist/devices/solarPower.d.ts +40 -0
- package/dist/devices/solarPower.d.ts.map +1 -0
- package/dist/devices/solarPower.js +38 -0
- package/dist/devices/solarPower.js.map +1 -0
- package/dist/devices/speaker.d.ts +87 -0
- package/dist/devices/speaker.d.ts.map +1 -0
- package/dist/devices/speaker.js +84 -0
- package/dist/devices/speaker.js.map +1 -0
- package/dist/devices/temperatureControl.d.ts +166 -0
- package/dist/devices/temperatureControl.d.ts.map +1 -0
- package/dist/devices/temperatureControl.js +24 -3
- package/dist/devices/temperatureControl.js.map +1 -0
- package/dist/devices/waterHeater.d.ts +111 -0
- package/dist/devices/waterHeater.d.ts.map +1 -0
- package/dist/devices/waterHeater.js +82 -2
- package/dist/devices/waterHeater.js.map +1 -0
- package/dist/dgram/coap.d.ts +205 -0
- package/dist/dgram/coap.d.ts.map +1 -0
- package/dist/dgram/coap.js +126 -13
- package/dist/dgram/coap.js.map +1 -0
- package/dist/dgram/dgram.d.ts +141 -0
- package/dist/dgram/dgram.d.ts.map +1 -0
- package/dist/dgram/dgram.js +114 -2
- package/dist/dgram/dgram.js.map +1 -0
- package/dist/dgram/mb_coap.d.ts +24 -0
- package/dist/dgram/mb_coap.d.ts.map +1 -0
- package/dist/dgram/mb_coap.js +41 -3
- package/dist/dgram/mb_coap.js.map +1 -0
- package/dist/dgram/mb_mdns.d.ts +24 -0
- package/dist/dgram/mb_mdns.d.ts.map +1 -0
- package/dist/dgram/mb_mdns.js +80 -15
- package/dist/dgram/mb_mdns.js.map +1 -0
- package/dist/dgram/mdns.d.ts +290 -0
- package/dist/dgram/mdns.d.ts.map +1 -0
- package/dist/dgram/mdns.js +299 -137
- package/dist/dgram/mdns.js.map +1 -0
- package/dist/dgram/multicast.d.ts +67 -0
- package/dist/dgram/multicast.d.ts.map +1 -0
- package/dist/dgram/multicast.js +62 -1
- package/dist/dgram/multicast.js.map +1 -0
- package/dist/dgram/unicast.d.ts +56 -0
- package/dist/dgram/unicast.d.ts.map +1 -0
- package/dist/dgram/unicast.js +54 -0
- package/dist/dgram/unicast.js.map +1 -0
- package/dist/frontend.d.ts +238 -0
- package/dist/frontend.d.ts.map +1 -0
- package/dist/frontend.js +455 -35
- package/dist/frontend.js.map +1 -0
- package/dist/frontendTypes.d.ts +529 -0
- package/dist/frontendTypes.d.ts.map +1 -0
- package/dist/frontendTypes.js +45 -0
- package/dist/frontendTypes.js.map +1 -0
- package/dist/helpers.d.ts +48 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +53 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/jestutils/export.d.ts +2 -0
- package/dist/jestutils/export.d.ts.map +1 -0
- package/dist/jestutils/export.js +1 -0
- package/dist/jestutils/export.js.map +1 -0
- package/dist/jestutils/jestHelpers.d.ts +340 -0
- package/dist/jestutils/jestHelpers.d.ts.map +1 -0
- package/dist/jestutils/jestHelpers.js +373 -13
- package/dist/jestutils/jestHelpers.js.map +1 -0
- package/dist/logger/export.d.ts +2 -0
- package/dist/logger/export.d.ts.map +1 -0
- package/dist/logger/export.js +1 -0
- package/dist/logger/export.js.map +1 -0
- package/dist/matter/behaviors.d.ts +2 -0
- package/dist/matter/behaviors.d.ts.map +1 -0
- package/dist/matter/behaviors.js +2 -0
- package/dist/matter/behaviors.js.map +1 -0
- package/dist/matter/clusters.d.ts +2 -0
- package/dist/matter/clusters.d.ts.map +1 -0
- package/dist/matter/clusters.js +2 -0
- package/dist/matter/clusters.js.map +1 -0
- package/dist/matter/devices.d.ts +2 -0
- package/dist/matter/devices.d.ts.map +1 -0
- package/dist/matter/devices.js +2 -0
- package/dist/matter/devices.js.map +1 -0
- package/dist/matter/endpoints.d.ts +2 -0
- package/dist/matter/endpoints.d.ts.map +1 -0
- package/dist/matter/endpoints.js +2 -0
- package/dist/matter/endpoints.js.map +1 -0
- package/dist/matter/export.d.ts +5 -0
- package/dist/matter/export.d.ts.map +1 -0
- package/dist/matter/export.js +3 -0
- package/dist/matter/export.js.map +1 -0
- package/dist/matter/types.d.ts +3 -0
- package/dist/matter/types.d.ts.map +1 -0
- package/dist/matter/types.js +3 -0
- package/dist/matter/types.js.map +1 -0
- package/dist/matterNode.d.ts +342 -0
- package/dist/matterNode.d.ts.map +1 -0
- package/dist/matterNode.js +369 -8
- package/dist/matterNode.js.map +1 -0
- package/dist/matterbridge.d.ts +493 -0
- package/dist/matterbridge.d.ts.map +1 -0
- package/dist/matterbridge.js +807 -46
- package/dist/matterbridge.js.map +1 -0
- package/dist/matterbridgeAccessoryPlatform.d.ts +41 -0
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -0
- package/dist/matterbridgeAccessoryPlatform.js +38 -0
- package/dist/matterbridgeAccessoryPlatform.js.map +1 -0
- package/dist/matterbridgeBehaviors.d.ts +2404 -0
- package/dist/matterbridgeBehaviors.d.ts.map +1 -0
- package/dist/matterbridgeBehaviors.js +68 -5
- package/dist/matterbridgeBehaviors.js.map +1 -0
- package/dist/matterbridgeDeviceTypes.d.ts +698 -0
- package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
- package/dist/matterbridgeDeviceTypes.js +635 -14
- package/dist/matterbridgeDeviceTypes.js.map +1 -0
- package/dist/matterbridgeDynamicPlatform.d.ts +41 -0
- package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -0
- package/dist/matterbridgeDynamicPlatform.js +38 -0
- package/dist/matterbridgeDynamicPlatform.js.map +1 -0
- package/dist/matterbridgeEndpoint.d.ts +1507 -0
- package/dist/matterbridgeEndpoint.d.ts.map +1 -0
- package/dist/matterbridgeEndpoint.js +1444 -53
- package/dist/matterbridgeEndpoint.js.map +1 -0
- package/dist/matterbridgeEndpointHelpers.d.ts +787 -0
- package/dist/matterbridgeEndpointHelpers.d.ts.map +1 -0
- package/dist/matterbridgeEndpointHelpers.js +483 -20
- package/dist/matterbridgeEndpointHelpers.js.map +1 -0
- package/dist/matterbridgeEndpointTypes.d.ts +166 -0
- package/dist/matterbridgeEndpointTypes.d.ts.map +1 -0
- package/dist/matterbridgeEndpointTypes.js +25 -0
- package/dist/matterbridgeEndpointTypes.js.map +1 -0
- package/dist/matterbridgePlatform.d.ts +537 -0
- package/dist/matterbridgePlatform.d.ts.map +1 -0
- package/dist/matterbridgePlatform.js +450 -1
- package/dist/matterbridgePlatform.js.map +1 -0
- package/dist/matterbridgeTypes.d.ts +251 -0
- package/dist/matterbridgeTypes.d.ts.map +1 -0
- package/dist/matterbridgeTypes.js +26 -0
- package/dist/matterbridgeTypes.js.map +1 -0
- package/dist/pluginManager.d.ts +372 -0
- package/dist/pluginManager.d.ts.map +1 -0
- package/dist/pluginManager.js +341 -5
- package/dist/pluginManager.js.map +1 -0
- package/dist/shelly.d.ts +181 -0
- package/dist/shelly.d.ts.map +1 -0
- package/dist/shelly.js +178 -7
- package/dist/shelly.js.map +1 -0
- package/dist/storage/export.d.ts +2 -0
- package/dist/storage/export.d.ts.map +1 -0
- package/dist/storage/export.js +1 -0
- package/dist/storage/export.js.map +1 -0
- package/dist/update.d.ts +84 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +93 -1
- package/dist/update.js.map +1 -0
- package/dist/utils/colorUtils.d.ts +101 -0
- package/dist/utils/colorUtils.d.ts.map +1 -0
- package/dist/utils/colorUtils.js +97 -2
- package/dist/utils/colorUtils.js.map +1 -0
- package/dist/utils/commandLine.d.ts +66 -0
- package/dist/utils/commandLine.d.ts.map +1 -0
- package/dist/utils/commandLine.js +60 -0
- package/dist/utils/commandLine.js.map +1 -0
- package/dist/utils/copyDirectory.d.ts +35 -0
- package/dist/utils/copyDirectory.d.ts.map +1 -0
- package/dist/utils/copyDirectory.js +37 -0
- package/dist/utils/copyDirectory.js.map +1 -0
- package/dist/utils/createDirectory.d.ts +34 -0
- package/dist/utils/createDirectory.d.ts.map +1 -0
- package/dist/utils/createDirectory.js +33 -0
- package/dist/utils/createDirectory.js.map +1 -0
- package/dist/utils/createZip.d.ts +39 -0
- package/dist/utils/createZip.d.ts.map +1 -0
- package/dist/utils/createZip.js +47 -2
- package/dist/utils/createZip.js.map +1 -0
- package/dist/utils/deepCopy.d.ts +32 -0
- package/dist/utils/deepCopy.d.ts.map +1 -0
- package/dist/utils/deepCopy.js +39 -0
- package/dist/utils/deepCopy.js.map +1 -0
- package/dist/utils/deepEqual.d.ts +54 -0
- package/dist/utils/deepEqual.d.ts.map +1 -0
- package/dist/utils/deepEqual.js +72 -1
- package/dist/utils/deepEqual.js.map +1 -0
- package/dist/utils/error.d.ts +45 -0
- package/dist/utils/error.d.ts.map +1 -0
- package/dist/utils/error.js +42 -0
- package/dist/utils/error.js.map +1 -0
- package/dist/utils/export.d.ts +13 -0
- package/dist/utils/export.d.ts.map +1 -0
- package/dist/utils/export.js +1 -0
- package/dist/utils/export.js.map +1 -0
- package/dist/utils/format.d.ts +53 -0
- package/dist/utils/format.d.ts.map +1 -0
- package/dist/utils/format.js +49 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/hex.d.ts +89 -0
- package/dist/utils/hex.d.ts.map +1 -0
- package/dist/utils/hex.js +124 -0
- package/dist/utils/hex.js.map +1 -0
- package/dist/utils/inspector.d.ts +87 -0
- package/dist/utils/inspector.d.ts.map +1 -0
- package/dist/utils/inspector.js +69 -1
- package/dist/utils/inspector.js.map +1 -0
- package/dist/utils/isvalid.d.ts +103 -0
- package/dist/utils/isvalid.d.ts.map +1 -0
- package/dist/utils/isvalid.js +101 -0
- package/dist/utils/isvalid.js.map +1 -0
- package/dist/utils/network.d.ts +111 -0
- package/dist/utils/network.d.ts.map +1 -0
- package/dist/utils/network.js +96 -5
- package/dist/utils/network.js.map +1 -0
- package/dist/utils/spawn.d.ts +33 -0
- package/dist/utils/spawn.d.ts.map +1 -0
- package/dist/utils/spawn.js +71 -1
- package/dist/utils/spawn.js.map +1 -0
- package/dist/utils/tracker.d.ts +108 -0
- package/dist/utils/tracker.d.ts.map +1 -0
- package/dist/utils/tracker.js +64 -1
- package/dist/utils/tracker.js.map +1 -0
- package/dist/utils/wait.d.ts +54 -0
- package/dist/utils/wait.d.ts.map +1 -0
- package/dist/utils/wait.js +60 -8
- package/dist/utils/wait.js.map +1 -0
- package/dist/workerGlobalPrefix.d.ts +25 -0
- package/dist/workerGlobalPrefix.d.ts.map +1 -0
- package/dist/workerGlobalPrefix.js +37 -5
- package/dist/workerGlobalPrefix.js.map +1 -0
- package/dist/workerTypes.d.ts +52 -0
- package/dist/workerTypes.d.ts.map +1 -0
- package/dist/workerTypes.js +24 -0
- package/dist/workerTypes.js.map +1 -0
- package/dist/workers.d.ts +69 -0
- package/dist/workers.d.ts.map +1 -0
- package/dist/workers.js +68 -4
- package/dist/workers.js.map +1 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +2 -1
|
@@ -1,13 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @description This file contains the Jest helpers.
|
|
3
|
+
* @file src/helpers.test.ts
|
|
4
|
+
* @author Luca Liguori
|
|
5
|
+
* @created 2025-09-03
|
|
6
|
+
* @version 1.0.14
|
|
7
|
+
* @license Apache-2.0
|
|
8
|
+
*
|
|
9
|
+
* Copyright 2025, 2026, 2027 Luca Liguori.
|
|
10
|
+
*
|
|
11
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
+
* you may not use this file except in compliance with the License.
|
|
13
|
+
* You may obtain a copy of the License at
|
|
14
|
+
*
|
|
15
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
+
*
|
|
17
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
+
* See the License for the specific language governing permissions and
|
|
21
|
+
* limitations under the License.
|
|
22
|
+
*/
|
|
1
23
|
import { rmSync } from 'node:fs';
|
|
2
24
|
import { inspect } from 'node:util';
|
|
3
25
|
import path from 'node:path';
|
|
26
|
+
// Imports from node-ansi-logger
|
|
4
27
|
import { AnsiLogger, er, rs, UNDERLINE, UNDERLINEOFF } from 'node-ansi-logger';
|
|
28
|
+
// Imports from @matter
|
|
5
29
|
import { LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, Environment, Lifecycle } from '@matter/general';
|
|
6
30
|
import { Endpoint, ServerNode, ServerNodeStore } from '@matter/node';
|
|
7
31
|
import { DeviceTypeId, VendorId } from '@matter/types/datatype';
|
|
8
32
|
import { AggregatorEndpoint } from '@matter/node/endpoints';
|
|
9
33
|
import { MdnsService } from '@matter/main/protocol';
|
|
10
34
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
35
|
+
// Imports from Matterbridge
|
|
11
36
|
import { Matterbridge } from '../matterbridge.js';
|
|
12
37
|
import { MATTER_STORAGE_NAME, NODE_STORAGE_DIR } from '../matterbridgeTypes.js';
|
|
13
38
|
import { bridge } from '../matterbridgeDeviceTypes.js';
|
|
@@ -16,6 +41,7 @@ import { Frontend } from '../frontend.js';
|
|
|
16
41
|
import { BroadcastServer } from '../broadcastServer.js';
|
|
17
42
|
export const originalProcessArgv = Object.freeze([...process.argv]);
|
|
18
43
|
export const originalProcessEnv = Object.freeze({ ...process.env });
|
|
44
|
+
// Spy on logger methods
|
|
19
45
|
export let loggerLogSpy;
|
|
20
46
|
export let loggerDebugSpy;
|
|
21
47
|
export let loggerInfoSpy;
|
|
@@ -23,15 +49,18 @@ export let loggerNoticeSpy;
|
|
|
23
49
|
export let loggerWarnSpy;
|
|
24
50
|
export let loggerErrorSpy;
|
|
25
51
|
export let loggerFatalSpy;
|
|
52
|
+
// Spy on console methods
|
|
26
53
|
export let consoleLogSpy;
|
|
27
54
|
export let consoleDebugSpy;
|
|
28
55
|
export let consoleInfoSpy;
|
|
29
56
|
export let consoleWarnSpy;
|
|
30
57
|
export let consoleErrorSpy;
|
|
58
|
+
// Spy on Matterbridge methods
|
|
31
59
|
export let addBridgedEndpointSpy;
|
|
32
60
|
export let removeBridgedEndpointSpy;
|
|
33
61
|
export let removeAllBridgedEndpointsSpy;
|
|
34
62
|
export let addVirtualEndpointSpy;
|
|
63
|
+
// Spy on PluginManager methods
|
|
35
64
|
export let installPluginSpy;
|
|
36
65
|
export let uninstallPluginSpy;
|
|
37
66
|
export let addPluginSpy;
|
|
@@ -42,12 +71,14 @@ export let shutdownPluginSpy;
|
|
|
42
71
|
export let removePluginSpy;
|
|
43
72
|
export let enablePluginSpy;
|
|
44
73
|
export let disablePluginSpy;
|
|
74
|
+
// Spy on Frontend methods
|
|
45
75
|
export let wssSendSnackbarMessageSpy;
|
|
46
76
|
export let wssSendCloseSnackbarMessageSpy;
|
|
47
77
|
export let wssSendUpdateRequiredSpy;
|
|
48
78
|
export let wssSendRefreshRequiredSpy;
|
|
49
79
|
export let wssSendRestartRequiredSpy;
|
|
50
80
|
export let wssSendRestartNotRequiredSpy;
|
|
81
|
+
// Spy on BroadcastServer methods
|
|
51
82
|
export let broadcastServerIsWorkerRequestSpy;
|
|
52
83
|
export let broadcastServerIsWorkerResponseSpy;
|
|
53
84
|
export let broadcastServerBroadcastSpy;
|
|
@@ -65,12 +96,29 @@ export let environment;
|
|
|
65
96
|
export let server;
|
|
66
97
|
export let aggregator;
|
|
67
98
|
export let log;
|
|
99
|
+
/**
|
|
100
|
+
* Setup the Jest environment:
|
|
101
|
+
* - it will remove any existing home directory
|
|
102
|
+
* - setup the spies for logging
|
|
103
|
+
*
|
|
104
|
+
* @param {string} name The name of the test suite.
|
|
105
|
+
* @param {boolean} debug If true, the logging is not mocked.
|
|
106
|
+
*
|
|
107
|
+
* @example
|
|
108
|
+
* ```typescript
|
|
109
|
+
* import { consoleDebugSpy, consoleErrorSpy, consoleInfoSpy, consoleLogSpy, consoleWarnSpy, loggerLogSpy, setDebug, setupTest } from './jestutils/jestHelpers.js';
|
|
110
|
+
*
|
|
111
|
+
* // Setup the test environment
|
|
112
|
+
* await setupTest(NAME, false);
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
68
115
|
export async function setupTest(name, debug = false) {
|
|
69
116
|
expect(name).toBeDefined();
|
|
70
117
|
expect(typeof name).toBe('string');
|
|
71
118
|
expect(name.length).toBeGreaterThanOrEqual(4);
|
|
72
119
|
NAME = name;
|
|
73
120
|
HOMEDIR = path.join('jest', name);
|
|
121
|
+
// Cleanup any existing home directory
|
|
74
122
|
rmSync(HOMEDIR, { recursive: true, force: true });
|
|
75
123
|
const { jest } = await import('@jest/globals');
|
|
76
124
|
loggerDebugSpy = jest.spyOn(AnsiLogger.prototype, 'debug');
|
|
@@ -121,8 +169,26 @@ export async function setupTest(name, debug = false) {
|
|
|
121
169
|
broadcastServerRequestSpy = jest.spyOn(BroadcastServer.prototype, 'request');
|
|
122
170
|
broadcastServerRespondSpy = jest.spyOn(BroadcastServer.prototype, 'respond');
|
|
123
171
|
broadcastServerFetchSpy = jest.spyOn(BroadcastServer.prototype, 'fetch');
|
|
172
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
124
173
|
broadcastMessageHandlerSpy = jest.spyOn(BroadcastServer.prototype, 'broadcastMessageHandler');
|
|
125
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* Set or unset the debug mode.
|
|
177
|
+
*
|
|
178
|
+
* @param {boolean} debug If true, the logging is not mocked.
|
|
179
|
+
* @returns {Promise<void>} A promise that resolves when the debug mode is set.
|
|
180
|
+
*
|
|
181
|
+
* @example
|
|
182
|
+
* ```typescript
|
|
183
|
+
* // Set the debug mode in test environment
|
|
184
|
+
* await setDebug(true);
|
|
185
|
+
* ```
|
|
186
|
+
*
|
|
187
|
+
* ```typescript
|
|
188
|
+
* // Reset the debug mode in test environment
|
|
189
|
+
* await setDebug(false);
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
126
192
|
export async function setDebug(debug) {
|
|
127
193
|
const { jest } = await import('@jest/globals');
|
|
128
194
|
if (debug) {
|
|
@@ -148,18 +214,41 @@ export async function setDebug(debug) {
|
|
|
148
214
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
149
215
|
}
|
|
150
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Create and start a fully initialized Matterbridge instance for testing.
|
|
219
|
+
*
|
|
220
|
+
* @param {('bridge' | 'childbridge' | 'controller' | '')} bridgeMode The bridge mode to start the Matterbridge instance in.
|
|
221
|
+
* @param {number} frontendPort The frontend port number.
|
|
222
|
+
* @param {number} matterPort The matter port number.
|
|
223
|
+
* @param {number} passcode The passcode number.
|
|
224
|
+
* @param {number} discriminator The discriminator number.
|
|
225
|
+
* @param {number} pluginSize The expected number of plugins.
|
|
226
|
+
* @param {number} devicesSize The expected number of devices.
|
|
227
|
+
* @returns {Promise<Matterbridge>} The Matterbridge instance.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* // Create and start a fully initialized Matterbridge instance for testing.
|
|
232
|
+
* await startMatterbridge();
|
|
233
|
+
* ```
|
|
234
|
+
*/
|
|
151
235
|
export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 8283, matterPort = 5540, passcode = 20252026, discriminator = 3840, pluginSize = 0, devicesSize = 0) {
|
|
236
|
+
// Set the environment variables
|
|
152
237
|
process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS'] = '100';
|
|
153
238
|
process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS'] = '100';
|
|
239
|
+
// Setup the process arguments
|
|
154
240
|
process.argv.length = 0;
|
|
155
241
|
process.argv.push(...originalProcessArgv, '-novirtual', '-debug', '-verbose', '-logger', 'debug', '-matterlogger', 'debug', bridgeMode === '' ? '-test' : '-' + bridgeMode, '-homedir', HOMEDIR, '-frontend', frontendPort.toString(), '-port', matterPort.toString(), '-passcode', passcode.toString(), '-discriminator', discriminator.toString());
|
|
242
|
+
// Load Matterbridge instance and initialize it
|
|
156
243
|
matterbridge = await Matterbridge.loadInstance(true);
|
|
157
244
|
expect(matterbridge).toBeDefined();
|
|
158
245
|
expect(matterbridge.profile).toBeUndefined();
|
|
159
246
|
expect(matterbridge.bridgeMode).toBe(bridgeMode);
|
|
247
|
+
// Get the frontend, plugins and devices
|
|
160
248
|
frontend = matterbridge.frontend;
|
|
161
249
|
plugins = matterbridge.plugins;
|
|
162
250
|
devices = matterbridge.devices;
|
|
251
|
+
// @ts-expect-error - access to private member for testing
|
|
163
252
|
expect(matterbridge.initialized).toBeTruthy();
|
|
164
253
|
expect(matterbridge.log).toBeDefined();
|
|
165
254
|
expect(matterbridge.rootDirectory).toBe(path.resolve('./'));
|
|
@@ -172,10 +261,15 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
|
|
|
172
261
|
expect(devices).toBeDefined();
|
|
173
262
|
expect(devices.size).toBe(devicesSize);
|
|
174
263
|
expect(frontend).toBeDefined();
|
|
264
|
+
// @ts-expect-error - access to private member for testing
|
|
175
265
|
expect(frontend.listening).toBeTruthy();
|
|
266
|
+
// @ts-expect-error - access to private member for testing
|
|
176
267
|
expect(frontend.httpServer).toBeDefined();
|
|
268
|
+
// @ts-expect-error - access to private member for testing
|
|
177
269
|
expect(frontend.httpsServer).toBeUndefined();
|
|
270
|
+
// @ts-expect-error - access to private member for testing
|
|
178
271
|
expect(frontend.expressApp).toBeDefined();
|
|
272
|
+
// @ts-expect-error - access to private member for testing
|
|
179
273
|
expect(frontend.webSocketServer).toBeDefined();
|
|
180
274
|
expect(matterbridge.nodeStorage).toBeDefined();
|
|
181
275
|
expect(matterbridge.nodeContext).toBeDefined();
|
|
@@ -213,25 +307,51 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
|
|
|
213
307
|
});
|
|
214
308
|
});
|
|
215
309
|
}
|
|
216
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("info"
|
|
310
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("info" /* LogLevel.INFO */, `The frontend http server is listening on ${UNDERLINE}http://${matterbridge.systemInformation.ipv4Address}:${frontendPort}${UNDERLINEOFF}${rs}`);
|
|
217
311
|
if (bridgeMode === 'bridge') {
|
|
218
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
219
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
220
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
221
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
222
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
312
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Starting Matterbridge server node`);
|
|
313
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Server node for Matterbridge is online`);
|
|
314
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in bridge mode...`);
|
|
315
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in bridge mode`);
|
|
316
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge bridge started successfully`);
|
|
223
317
|
}
|
|
224
318
|
else if (bridgeMode === 'childbridge') {
|
|
225
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
226
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
227
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
319
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in childbridge mode...`);
|
|
320
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in childbridge mode`);
|
|
321
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge childbridge started successfully`);
|
|
228
322
|
}
|
|
229
323
|
return matterbridge;
|
|
230
324
|
}
|
|
325
|
+
/**
|
|
326
|
+
* Stop the fully initialized Matterbridge instance.
|
|
327
|
+
*
|
|
328
|
+
* @param {cleanupPause} cleanupPause The pause duration before cleanup. Default is 10 ms.
|
|
329
|
+
* @param {destroyPause} destroyPause The pause duration before destruction. Default is 250 ms.
|
|
330
|
+
*
|
|
331
|
+
* @example
|
|
332
|
+
* ```typescript
|
|
333
|
+
* // Stop the fully initialized Matterbridge instance.
|
|
334
|
+
* await stopMatterbridge();
|
|
335
|
+
* ```
|
|
336
|
+
*/
|
|
231
337
|
export async function stopMatterbridge(cleanupPause = 10, destroyPause = 250) {
|
|
232
338
|
await destroyMatterbridgeEnvironment(cleanupPause, destroyPause);
|
|
233
339
|
}
|
|
340
|
+
/**
|
|
341
|
+
* Create a Matterbridge instance for testing without initializing it.
|
|
342
|
+
*
|
|
343
|
+
* @param {string} name - Name for the environment (jest/name).
|
|
344
|
+
* @returns {Promise<Matterbridge>} The Matterbridge instance.
|
|
345
|
+
*
|
|
346
|
+
* @example
|
|
347
|
+
* ```typescript
|
|
348
|
+
* // Create Matterbridge environment
|
|
349
|
+
* await createMatterbridgeEnvironment(NAME);
|
|
350
|
+
* await startMatterbridgeEnvironment(MATTER_PORT);
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
234
353
|
export async function createMatterbridgeEnvironment(name) {
|
|
354
|
+
// Create a MatterbridgeEdge instance
|
|
235
355
|
matterbridge = await Matterbridge.loadInstance(false);
|
|
236
356
|
expect(matterbridge).toBeDefined();
|
|
237
357
|
expect(matterbridge).toBeInstanceOf(Matterbridge);
|
|
@@ -242,25 +362,52 @@ export async function createMatterbridgeEnvironment(name) {
|
|
|
242
362
|
matterbridge.matterbridgeDirectory = path.join('jest', name, '.matterbridge');
|
|
243
363
|
matterbridge.matterbridgePluginDirectory = path.join('jest', name, 'Matterbridge');
|
|
244
364
|
matterbridge.matterbridgeCertDirectory = path.join('jest', name, '.mattercert');
|
|
245
|
-
matterbridge.log.logLevel = "debug"
|
|
246
|
-
log = new AnsiLogger({ logName: 'Plugin platform', logTimestampFormat: 4
|
|
365
|
+
matterbridge.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
366
|
+
log = new AnsiLogger({ logName: 'Plugin platform', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
367
|
+
// Get the frontend, plugins and devices
|
|
368
|
+
frontend = matterbridge.frontend;
|
|
369
|
+
plugins = matterbridge.plugins;
|
|
370
|
+
devices = matterbridge.devices;
|
|
371
|
+
// Setup matter environment
|
|
372
|
+
// @ts-expect-error - access to private member for testing
|
|
247
373
|
matterbridge.environment = createTestEnvironment(name);
|
|
374
|
+
// @ts-expect-error - access to private member for testing
|
|
248
375
|
expect(matterbridge.environment).toBeDefined();
|
|
376
|
+
// @ts-expect-error - access to private member for testing
|
|
249
377
|
expect(matterbridge.environment).toBeInstanceOf(Environment);
|
|
250
378
|
return matterbridge;
|
|
251
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Start the matterbridge environment.
|
|
382
|
+
* Only node storage, matter storage and the server and aggregator nodes are started.
|
|
383
|
+
*
|
|
384
|
+
* @param {number} port The matter server port.
|
|
385
|
+
* @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} The started server and aggregator.
|
|
386
|
+
*
|
|
387
|
+
* @example
|
|
388
|
+
* ```typescript
|
|
389
|
+
* // Create Matterbridge environment
|
|
390
|
+
* await createMatterbridgeEnvironment(NAME);
|
|
391
|
+
* await startMatterbridgeEnvironment(MATTER_PORT);
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
252
394
|
export async function startMatterbridgeEnvironment(port = 5540) {
|
|
395
|
+
// Create the node storage
|
|
253
396
|
matterbridge.nodeStorage = new NodeStorageManager({ dir: path.join(matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
254
397
|
matterbridge.nodeContext = await matterbridge.nodeStorage.createStorage('matterbridge');
|
|
398
|
+
// Create the matter storage
|
|
399
|
+
// @ts-expect-error - access to private member for testing
|
|
255
400
|
await matterbridge.startMatterStorage();
|
|
256
401
|
expect(matterbridge.matterStorageService).toBeDefined();
|
|
257
402
|
expect(matterbridge.matterStorageManager).toBeDefined();
|
|
258
403
|
expect(matterbridge.matterbridgeContext).toBeDefined();
|
|
404
|
+
// @ts-expect-error - access to private member for testing
|
|
259
405
|
server = await matterbridge.createServerNode(matterbridge.matterbridgeContext, port);
|
|
260
406
|
expect(server).toBeDefined();
|
|
261
407
|
expect(server).toBeDefined();
|
|
262
408
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
263
409
|
matterbridge.serverNode = server;
|
|
410
|
+
// @ts-expect-error - access to private member for testing
|
|
264
411
|
aggregator = await matterbridge.createAggregatorNode(matterbridge.matterbridgeContext);
|
|
265
412
|
expect(aggregator).toBeDefined();
|
|
266
413
|
matterbridge.aggregatorNode = aggregator;
|
|
@@ -268,6 +415,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
268
415
|
expect(server.parts.has(aggregator.id)).toBeTruthy();
|
|
269
416
|
expect(server.parts.has(aggregator)).toBeTruthy();
|
|
270
417
|
expect(aggregator.lifecycle.isReady).toBeTruthy();
|
|
418
|
+
// Wait for the server to be online
|
|
271
419
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
272
420
|
await new Promise((resolve) => {
|
|
273
421
|
server.lifecycle.online.on(async () => {
|
|
@@ -275,6 +423,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
275
423
|
});
|
|
276
424
|
server.start();
|
|
277
425
|
});
|
|
426
|
+
// Check if the server is online
|
|
278
427
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
279
428
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
280
429
|
expect(server.lifecycle.isCommissioned).toBeFalsy();
|
|
@@ -286,11 +435,27 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
286
435
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
287
436
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
288
437
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
438
|
+
// Ensure the queue is empty and pause
|
|
289
439
|
await flushAsync();
|
|
290
440
|
return [server, aggregator];
|
|
291
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* Add a matterbridge platform for testing.
|
|
444
|
+
*
|
|
445
|
+
* @param {MatterbridgePlatform} platform The platform to add.
|
|
446
|
+
* @param {string} [name] Optional name of the platform.
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```typescript
|
|
450
|
+
* platform = new Platform(matterbridge, log, config);
|
|
451
|
+
* // Add the platform to the Matterbridge environment
|
|
452
|
+
* addMatterbridgePlatform(platform);
|
|
453
|
+
* ```
|
|
454
|
+
*/
|
|
292
455
|
export function addMatterbridgePlatform(platform, name) {
|
|
293
456
|
expect(platform).toBeDefined();
|
|
457
|
+
// Setup the platform MatterNode helpers
|
|
458
|
+
// @ts-expect-error - setMatterNode is intentionally private
|
|
294
459
|
platform.setMatterNode?.(matterbridge.addBridgedEndpoint.bind(matterbridge), matterbridge.removeBridgedEndpoint.bind(matterbridge), matterbridge.removeAllBridgedEndpoints.bind(matterbridge), matterbridge.addVirtualEndpoint.bind(matterbridge));
|
|
295
460
|
if (name)
|
|
296
461
|
platform.config.name = name;
|
|
@@ -301,6 +466,7 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
301
466
|
expect(platform.version).toBeDefined();
|
|
302
467
|
expect(platform.config.debug).toBeDefined();
|
|
303
468
|
expect(platform.config.unregisterOnShutdown).toBeDefined();
|
|
469
|
+
// @ts-expect-error accessing private member for testing
|
|
304
470
|
matterbridge.plugins._plugins.set(platform.config.name, {
|
|
305
471
|
name: platform.config.name,
|
|
306
472
|
path: './',
|
|
@@ -312,63 +478,150 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
312
478
|
});
|
|
313
479
|
platform['name'] = platform.config.name;
|
|
314
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Stop the matterbridge environment
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* // Destroy Matterbridge environment
|
|
487
|
+
* await stopMatterbridgeEnvironment();
|
|
488
|
+
* await destroyMatterbridgeEnvironment();
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
315
491
|
export async function stopMatterbridgeEnvironment() {
|
|
316
492
|
expect(matterbridge).toBeDefined();
|
|
317
493
|
expect(server).toBeDefined();
|
|
318
494
|
expect(aggregator).toBeDefined();
|
|
495
|
+
// Flush any pending endpoint number persistence
|
|
319
496
|
await flushAllEndpointNumberPersistence(server);
|
|
497
|
+
// Ensure all endpoint numbers are persisted
|
|
320
498
|
await assertAllEndpointNumbersPersisted(server);
|
|
499
|
+
// Close the server node
|
|
321
500
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
322
501
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
323
502
|
await server.close();
|
|
324
503
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
325
504
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
505
|
+
// Stop the matter storage
|
|
506
|
+
// @ts-expect-error - access to private member for testing
|
|
326
507
|
await matterbridge.stopMatterStorage();
|
|
327
508
|
expect(matterbridge.matterStorageService).not.toBeDefined();
|
|
328
509
|
expect(matterbridge.matterStorageManager).not.toBeDefined();
|
|
329
510
|
expect(matterbridge.matterbridgeContext).not.toBeDefined();
|
|
511
|
+
// Stop the node storage
|
|
330
512
|
await matterbridge.nodeContext?.close();
|
|
513
|
+
matterbridge.nodeContext = undefined;
|
|
331
514
|
await matterbridge.nodeStorage?.close();
|
|
515
|
+
matterbridge.nodeStorage = undefined;
|
|
516
|
+
// Ensure the queue is empty and pause
|
|
332
517
|
await flushAsync();
|
|
333
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* Destroy the matterbridge environment
|
|
521
|
+
*
|
|
522
|
+
* @param {number} cleanupPause The timeout for the destroy operation (default 10ms).
|
|
523
|
+
* @param {number} destroyPause The pause duration after cleanup before destroying the instance (default 250ms).
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```typescript
|
|
527
|
+
* // Destroy Matterbridge environment
|
|
528
|
+
* await stopMatterbridgeEnvironment();
|
|
529
|
+
* await destroyMatterbridgeEnvironment();
|
|
530
|
+
* ```
|
|
531
|
+
*/
|
|
334
532
|
export async function destroyMatterbridgeEnvironment(cleanupPause = 10, destroyPause = 250) {
|
|
533
|
+
// Destroy a matterbridge instance
|
|
335
534
|
await destroyInstance(matterbridge, cleanupPause, destroyPause);
|
|
535
|
+
// Close the mDNS service
|
|
336
536
|
await closeMdnsInstance(matterbridge);
|
|
537
|
+
// Reset the singleton instance
|
|
538
|
+
// @ts-expect-error - accessing private member for testing
|
|
337
539
|
Matterbridge.instance = undefined;
|
|
338
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Destroy a matterbridge instance
|
|
543
|
+
*
|
|
544
|
+
* @param {Matterbridge} matterbridge The matterbridge instance to destroy.
|
|
545
|
+
* @param {number} cleanupPause The pause duration to wait for the cleanup to complete in milliseconds (default 10ms).
|
|
546
|
+
* @param {number} destroyPause The pause duration to wait after cleanup before destroying the instance in milliseconds (default 250ms).
|
|
547
|
+
*/
|
|
339
548
|
export async function destroyInstance(matterbridge, cleanupPause = 10, destroyPause = 250) {
|
|
549
|
+
// Cleanup the Matterbridge instance
|
|
550
|
+
// @ts-expect-error - accessing private member for testing
|
|
340
551
|
await matterbridge.cleanup('destroying instance...', false, cleanupPause);
|
|
552
|
+
// Pause before destroying the instance
|
|
341
553
|
if (destroyPause > 0)
|
|
342
554
|
await flushAsync(undefined, undefined, destroyPause);
|
|
343
555
|
}
|
|
556
|
+
/**
|
|
557
|
+
* Close the mDNS instance in the matterbridge environment.
|
|
558
|
+
*
|
|
559
|
+
* @param {Matterbridge} matterbridge The matterbridge instance.
|
|
560
|
+
* @returns {Promise<void>} A promise that resolves when the mDNS instance is closed.
|
|
561
|
+
*/
|
|
344
562
|
export async function closeMdnsInstance(matterbridge) {
|
|
563
|
+
// TODO: matter.js 0.16.0 - provide close method to close the mDNS service
|
|
564
|
+
// @ts-expect-error - accessing private member for testing
|
|
565
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
345
566
|
const mdns = matterbridge.environment.get(MdnsService);
|
|
346
567
|
if (mdns && mdns[Symbol.asyncDispose] && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
347
568
|
await mdns[Symbol.asyncDispose]();
|
|
348
569
|
if (mdns && mdns.close && typeof mdns.close === 'function')
|
|
349
570
|
await mdns.close();
|
|
350
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Create a matter test environment for testing:
|
|
574
|
+
* - it will remove any existing home directory
|
|
575
|
+
* - setup the matter environment with name, debug logging and ANSI format
|
|
576
|
+
* - setup the mDNS service in the environment
|
|
577
|
+
*
|
|
578
|
+
* @param {string} name - Name for the environment (jest/name).
|
|
579
|
+
* @returns {Environment} - The default matter environment.
|
|
580
|
+
*/
|
|
351
581
|
export function createTestEnvironment(name) {
|
|
352
582
|
expect(name).toBeDefined();
|
|
353
583
|
expect(typeof name).toBe('string');
|
|
354
|
-
expect(name.length).toBeGreaterThanOrEqual(4);
|
|
584
|
+
expect(name.length).toBeGreaterThanOrEqual(4); // avoid accidental deletion of short paths like "/" or "C:\"
|
|
585
|
+
// Cleanup any existing home directory
|
|
355
586
|
rmSync(path.join('jest', name), { recursive: true, force: true });
|
|
587
|
+
// Setup the matter environment
|
|
356
588
|
environment = Environment.default;
|
|
357
589
|
environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
358
590
|
environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
359
591
|
environment.vars.set('path.root', path.join('jest', name, '.matterbridge', MATTER_STORAGE_NAME));
|
|
360
592
|
environment.vars.set('runtime.signals', false);
|
|
361
593
|
environment.vars.set('runtime.exitcode', false);
|
|
594
|
+
// Setup the mDNS service in the environment
|
|
362
595
|
new MdnsService(environment);
|
|
596
|
+
// await environment.get(MdnsService)?.construction.ready;
|
|
363
597
|
return environment;
|
|
364
598
|
}
|
|
599
|
+
/**
|
|
600
|
+
* Destroy the matter test environment by closing the mDNS service.
|
|
601
|
+
*
|
|
602
|
+
* @returns {Promise<void>} A promise that resolves when the test environment is destroyed.
|
|
603
|
+
*/
|
|
365
604
|
export async function destroyTestEnvironment() {
|
|
605
|
+
// stop the mDNS service
|
|
606
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
366
607
|
const mdns = environment.get(MdnsService);
|
|
367
608
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
368
609
|
await mdns[Symbol.asyncDispose]();
|
|
369
610
|
if (mdns && typeof mdns.close === 'function')
|
|
370
611
|
await mdns.close();
|
|
371
612
|
}
|
|
613
|
+
/**
|
|
614
|
+
* Advance the Node.js event loop deterministically to allow chained asynchronous work (Promises scheduled in
|
|
615
|
+
* microtasks and follow‑up macrotasks) to complete inside tests without adding arbitrary long timeouts.
|
|
616
|
+
*
|
|
617
|
+
* NOTE: This does not guarantee OS level network IO completion—only JavaScript task queue progression inside the
|
|
618
|
+
* current process.
|
|
619
|
+
*
|
|
620
|
+
* @param {number} ticks Number of macrotask (setImmediate) turns to yield (default 3).
|
|
621
|
+
* @param {number} microTurns Number of microtask drains (Promise.resolve chains) after macrotask yielding (default 10).
|
|
622
|
+
* @param {number} pause Final timer delay in ms; set 0 to disable (default 250ms).
|
|
623
|
+
* @returns {Promise<void>} Resolves after the requested event loop advancement has completed.
|
|
624
|
+
*/
|
|
372
625
|
export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
373
626
|
for (let i = 0; i < ticks; i++)
|
|
374
627
|
await new Promise((resolve) => setImmediate(resolve));
|
|
@@ -377,16 +630,33 @@ export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
|
377
630
|
if (pause)
|
|
378
631
|
await new Promise((resolve) => setTimeout(resolve, pause));
|
|
379
632
|
}
|
|
633
|
+
/**
|
|
634
|
+
* Summarize live libuv handles/requests inside a process.
|
|
635
|
+
*
|
|
636
|
+
* @param {AnsiLogger} log - Logger to use for output
|
|
637
|
+
*
|
|
638
|
+
* @returns {number} - The total number of active handles and requests
|
|
639
|
+
*/
|
|
380
640
|
export function logKeepAlives(log) {
|
|
641
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
642
|
const handles = process._getActiveHandles?.() ?? [];
|
|
643
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
382
644
|
const requests = process._getActiveRequests?.() ?? [];
|
|
645
|
+
// istanbul ignore next
|
|
383
646
|
const fmtHandle = (h, i) => {
|
|
384
647
|
const ctor = h?.constructor?.name ?? 'Unknown';
|
|
648
|
+
// Timer-like?
|
|
649
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
385
650
|
const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : undefined;
|
|
651
|
+
// MessagePort?
|
|
652
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
386
653
|
const isPort = h?.constructor?.name?.includes('MessagePort');
|
|
654
|
+
// Socket/Server?
|
|
655
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
387
656
|
const fd = h?.fd ?? h?._handle?.fd;
|
|
388
657
|
return { i, type: ctor, hasRef, isPort, fd };
|
|
389
658
|
};
|
|
659
|
+
// istanbul ignore next
|
|
390
660
|
const fmtReq = (r, i) => {
|
|
391
661
|
const ctor = r?.constructor?.name ?? 'Unknown';
|
|
392
662
|
return { i, type: ctor };
|
|
@@ -395,6 +665,7 @@ export function logKeepAlives(log) {
|
|
|
395
665
|
handles: handles.map(fmtHandle),
|
|
396
666
|
requests: requests.map(fmtReq),
|
|
397
667
|
};
|
|
668
|
+
// istanbul ignore next if
|
|
398
669
|
if (summary.handles.length === 0 && summary.requests.length === 0) {
|
|
399
670
|
log?.debug('KeepAlive: no active handles or requests.');
|
|
400
671
|
}
|
|
@@ -406,6 +677,19 @@ export function logKeepAlives(log) {
|
|
|
406
677
|
}
|
|
407
678
|
return summary.handles.length + summary.requests.length;
|
|
408
679
|
}
|
|
680
|
+
/**
|
|
681
|
+
* Flush (await) the lazy endpoint number persistence mechanism used by matter.js.
|
|
682
|
+
*
|
|
683
|
+
* Background:
|
|
684
|
+
* assignNumber() batches persistence (store.saveNumber + updating __nextNumber__) via an internal promise (#numbersPersisted).
|
|
685
|
+
* Calling endpointStores.close() waits for the current batch only. If new endpoints were added in the same macrotask
|
|
686
|
+
* cycle additional micro/macro turns might be needed to ensure the batch started. We defensively yield macrotasks
|
|
687
|
+
* (setImmediate) and then await close() multiple rounds.
|
|
688
|
+
*
|
|
689
|
+
* @param {ServerNode} targetServer The server whose endpoint numbering persistence should be flushed.
|
|
690
|
+
* @param {number} rounds Number of macrotask + close cycles to run (2 is usually sufficient; 1 often works).
|
|
691
|
+
* @returns {Promise<void>} Resolves when pending number persistence batches have completed.
|
|
692
|
+
*/
|
|
409
693
|
export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
|
|
410
694
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
411
695
|
for (let i = 0; i < rounds; i++) {
|
|
@@ -413,6 +697,12 @@ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2
|
|
|
413
697
|
await nodeStore.endpointStores.close();
|
|
414
698
|
}
|
|
415
699
|
}
|
|
700
|
+
/**
|
|
701
|
+
* Collect all endpoints in the server endpoint tree (root -> descendants).
|
|
702
|
+
*
|
|
703
|
+
* @param {Endpoint} root Root endpoint (typically the ServerNode root endpoint cast as Endpoint).
|
|
704
|
+
* @returns {Endpoint[]} Flat array including the root and every descendant once.
|
|
705
|
+
*/
|
|
416
706
|
function collectAllEndpoints(root) {
|
|
417
707
|
const list = [];
|
|
418
708
|
const walk = (ep) => {
|
|
@@ -426,14 +716,26 @@ function collectAllEndpoints(root) {
|
|
|
426
716
|
walk(root);
|
|
427
717
|
return list;
|
|
428
718
|
}
|
|
719
|
+
/**
|
|
720
|
+
* Assert that every endpoint attached to the server has an assigned and (batch-)persisted endpoint number.
|
|
721
|
+
*
|
|
722
|
+
* This waits for any outstanding number persistence batch (endpointStores.close()), then traverses the endpoint
|
|
723
|
+
* graph and asserts:
|
|
724
|
+
* - Root endpoint: number is 0 (allowing undefined to coerce to 0 via nullish coalescing check).
|
|
725
|
+
* - All other endpoints: number > 0.
|
|
726
|
+
*
|
|
727
|
+
* @param {ServerNode} targetServer The server whose endpoint numbers are verified.
|
|
728
|
+
* @returns {Promise<void>} Resolves when assertions complete.
|
|
729
|
+
*/
|
|
429
730
|
export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
430
731
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
732
|
+
// Ensure any pending persistence finished (flush any in-flight batch promise)
|
|
431
733
|
await nodeStore.endpointStores.close();
|
|
432
734
|
const all = collectAllEndpoints(targetServer);
|
|
433
735
|
for (const ep of all) {
|
|
434
736
|
const store = nodeStore.storeForEndpoint(ep);
|
|
435
737
|
if (ep.maybeNumber === 0) {
|
|
436
|
-
expect(store.number ?? 0).toBe(0);
|
|
738
|
+
expect(store.number ?? 0).toBe(0); // root
|
|
437
739
|
}
|
|
438
740
|
else {
|
|
439
741
|
expect(store.number).toBeGreaterThan(0);
|
|
@@ -441,23 +743,42 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
|
441
743
|
}
|
|
442
744
|
return all.length;
|
|
443
745
|
}
|
|
746
|
+
/**
|
|
747
|
+
* Close the server node stores to flush any pending endpoint number persistence.
|
|
748
|
+
*
|
|
749
|
+
* @param {ServerNode} targetServer The server whose endpoint stores should be closed.
|
|
750
|
+
* @returns {Promise<void>} Resolves when the stores have been closed.
|
|
751
|
+
*/
|
|
444
752
|
export async function closeServerNodeStores(targetServer) {
|
|
753
|
+
// Close endpoint stores to avoid number persistence issues
|
|
445
754
|
if (!targetServer)
|
|
446
755
|
targetServer = server;
|
|
447
756
|
await targetServer?.env.get(ServerNodeStore)?.endpointStores.close();
|
|
448
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Start a matter server node for testing.
|
|
760
|
+
*
|
|
761
|
+
* @param {string} name Name of the server (used for logging and product description).
|
|
762
|
+
* @param {number} port TCP port to listen on.
|
|
763
|
+
* @param {DeviceTypeId} deviceType Device type identifier for the server node.
|
|
764
|
+
* @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} Resolves to an array containing the created ServerNode and its AggregatorNode.
|
|
765
|
+
*/
|
|
449
766
|
export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
450
767
|
const { randomBytes } = await import('node:crypto');
|
|
451
768
|
const random = randomBytes(8).toString('hex');
|
|
769
|
+
// Create the server node
|
|
452
770
|
server = await ServerNode.create({
|
|
453
771
|
id: name + 'ServerNode',
|
|
772
|
+
// Provide the environment
|
|
454
773
|
environment,
|
|
774
|
+
// Provide Node announcement settings
|
|
455
775
|
productDescription: {
|
|
456
776
|
name: name + 'ServerNode',
|
|
457
777
|
deviceType: DeviceTypeId(deviceType),
|
|
458
778
|
vendorId: VendorId(0xfff1),
|
|
459
779
|
productId: 0x8000,
|
|
460
780
|
},
|
|
781
|
+
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
461
782
|
basicInformation: {
|
|
462
783
|
vendorId: VendorId(0xfff1),
|
|
463
784
|
vendorName: 'Matterbridge',
|
|
@@ -470,32 +791,39 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
470
791
|
serialNumber: 'SN' + random,
|
|
471
792
|
uniqueId: 'UI' + random,
|
|
472
793
|
},
|
|
794
|
+
// Provide Network relevant configuration like the port
|
|
473
795
|
network: {
|
|
474
796
|
listeningAddressIpv4: undefined,
|
|
475
797
|
listeningAddressIpv6: undefined,
|
|
476
798
|
port,
|
|
477
799
|
},
|
|
800
|
+
// Provide the certificate for the device
|
|
478
801
|
operationalCredentials: {
|
|
479
802
|
certification: undefined,
|
|
480
803
|
},
|
|
481
804
|
});
|
|
482
805
|
expect(server).toBeDefined();
|
|
483
806
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
807
|
+
// Create the aggregator node
|
|
484
808
|
aggregator = new Endpoint(AggregatorEndpoint, {
|
|
485
809
|
id: name + 'AggregatorNode',
|
|
486
810
|
});
|
|
487
811
|
expect(aggregator).toBeDefined();
|
|
812
|
+
// Add the aggregator to the server
|
|
488
813
|
await server.add(aggregator);
|
|
489
814
|
expect(server.parts.has(aggregator.id)).toBeTruthy();
|
|
490
815
|
expect(server.parts.has(aggregator)).toBeTruthy();
|
|
491
816
|
expect(aggregator.lifecycle.isReady).toBeTruthy();
|
|
817
|
+
// Run the server
|
|
492
818
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
819
|
+
// Wait for the server to be online
|
|
493
820
|
await new Promise((resolve) => {
|
|
494
821
|
server.lifecycle.online.on(async () => {
|
|
495
822
|
resolve();
|
|
496
823
|
});
|
|
497
824
|
server.start();
|
|
498
825
|
});
|
|
826
|
+
// Check if the server is online
|
|
499
827
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
500
828
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
501
829
|
expect(server.lifecycle.isCommissioned).toBeFalsy();
|
|
@@ -507,31 +835,53 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
507
835
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
508
836
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
509
837
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
838
|
+
// Ensure the queue is empty and pause 250ms
|
|
510
839
|
await flushAsync();
|
|
511
840
|
return [server, aggregator];
|
|
512
841
|
}
|
|
842
|
+
/**
|
|
843
|
+
* Stop a matter server node.
|
|
844
|
+
*
|
|
845
|
+
* @param {ServerNode<ServerNode.RootEndpoint>} server The server to stop.
|
|
846
|
+
* @returns {Promise<void>} Resolves when the server has stopped.
|
|
847
|
+
*/
|
|
513
848
|
export async function stopServerNode(server) {
|
|
849
|
+
// Flush any pending endpoint number persistence
|
|
514
850
|
await flushAllEndpointNumberPersistence(server);
|
|
851
|
+
// Ensure all endpoint numbers are persisted
|
|
515
852
|
await assertAllEndpointNumbersPersisted(server);
|
|
853
|
+
// Stop the server
|
|
516
854
|
expect(server).toBeDefined();
|
|
517
855
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
518
856
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
519
857
|
await server.close();
|
|
520
858
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
521
859
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
860
|
+
// stop the mDNS service
|
|
861
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
522
862
|
const mdns = environment.get(MdnsService);
|
|
523
863
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
524
864
|
await mdns[Symbol.asyncDispose]();
|
|
525
865
|
if (mdns && typeof mdns.close === 'function')
|
|
526
866
|
await mdns.close();
|
|
867
|
+
// Ensure the queue is empty and pause 250ms
|
|
527
868
|
await flushAsync();
|
|
528
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* Add a device (endpoint) to a matter server node or an aggregator.
|
|
872
|
+
*
|
|
873
|
+
* @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to add the device to.
|
|
874
|
+
* @param {Endpoint} device The device to add.
|
|
875
|
+
* @param {number} pause The pause time in milliseconds after addition (default 10ms).
|
|
876
|
+
* @returns {Promise<void>} Resolves when the device has been added and is ready.
|
|
877
|
+
*/
|
|
529
878
|
export async function addDevice(owner, device, pause = 10) {
|
|
530
879
|
expect(owner).toBeDefined();
|
|
531
880
|
expect(device).toBeDefined();
|
|
532
881
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
533
882
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
534
883
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
884
|
+
// istanbul ignore next
|
|
535
885
|
try {
|
|
536
886
|
await owner.add(device);
|
|
537
887
|
}
|
|
@@ -551,12 +901,21 @@ export async function addDevice(owner, device, pause = 10) {
|
|
|
551
901
|
await flushAsync(undefined, undefined, pause);
|
|
552
902
|
return true;
|
|
553
903
|
}
|
|
904
|
+
/**
|
|
905
|
+
* Delete a device (endpoint) from a matter server node or an aggregator.
|
|
906
|
+
*
|
|
907
|
+
* @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to remove the device from.
|
|
908
|
+
* @param {Endpoint} device The device to remove.
|
|
909
|
+
* @param {number} pause The pause time in milliseconds after deletion (default 10ms).
|
|
910
|
+
* @returns {Promise<void>} Resolves when the device has been removed and is no longer ready.
|
|
911
|
+
*/
|
|
554
912
|
export async function deleteDevice(owner, device, pause = 10) {
|
|
555
913
|
expect(owner).toBeDefined();
|
|
556
914
|
expect(device).toBeDefined();
|
|
557
915
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
558
916
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
559
917
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
918
|
+
// istanbul ignore next
|
|
560
919
|
try {
|
|
561
920
|
await device.delete();
|
|
562
921
|
}
|
|
@@ -576,3 +935,4 @@ export async function deleteDevice(owner, device, pause = 10) {
|
|
|
576
935
|
await flushAsync(undefined, undefined, pause);
|
|
577
936
|
return true;
|
|
578
937
|
}
|
|
938
|
+
//# sourceMappingURL=jestHelpers.js.map
|