matterbridge 3.4.3-dev-20251209-e6cb85f → 3.4.3
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/README.md +2 -3
- package/dist/broadcastServer.d.ts +144 -0
- package/dist/broadcastServer.d.ts.map +1 -0
- package/dist/broadcastServer.js +119 -0
- 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 +345 -0
- package/dist/jestutils/jestHelpers.d.ts.map +1 -0
- package/dist/jestutils/jestHelpers.js +371 -14
- 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 +492 -0
- package/dist/matterbridge.d.ts.map +1 -0
- package/dist/matterbridge.js +811 -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 +539 -0
- package/dist/matterbridgePlatform.d.ts.map +1 -0
- package/dist/matterbridgePlatform.js +451 -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
- package/scripts/data_model.mjs +2058 -0
|
@@ -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';
|
|
@@ -17,6 +42,7 @@ import { BroadcastServer } from '../broadcastServer.js';
|
|
|
17
42
|
import { MatterbridgeEndpoint } from '../matterbridgeEndpoint.js';
|
|
18
43
|
export const originalProcessArgv = Object.freeze([...process.argv]);
|
|
19
44
|
export const originalProcessEnv = Object.freeze({ ...process.env });
|
|
45
|
+
// Spy on logger methods
|
|
20
46
|
export let loggerLogSpy;
|
|
21
47
|
export let loggerDebugSpy;
|
|
22
48
|
export let loggerInfoSpy;
|
|
@@ -24,19 +50,23 @@ export let loggerNoticeSpy;
|
|
|
24
50
|
export let loggerWarnSpy;
|
|
25
51
|
export let loggerErrorSpy;
|
|
26
52
|
export let loggerFatalSpy;
|
|
53
|
+
// Spy on console methods
|
|
27
54
|
export let consoleLogSpy;
|
|
28
55
|
export let consoleDebugSpy;
|
|
29
56
|
export let consoleInfoSpy;
|
|
30
57
|
export let consoleWarnSpy;
|
|
31
58
|
export let consoleErrorSpy;
|
|
59
|
+
// Spy on Matterbridge methods
|
|
32
60
|
export let addBridgedEndpointSpy;
|
|
33
61
|
export let removeBridgedEndpointSpy;
|
|
34
62
|
export let removeAllBridgedEndpointsSpy;
|
|
35
63
|
export let addVirtualEndpointSpy;
|
|
64
|
+
// Spy on MatterbridgeEndpoint methods
|
|
36
65
|
export let setAttributeSpy;
|
|
37
66
|
export let updateAttributeSpy;
|
|
38
67
|
export let triggerEventSpy;
|
|
39
68
|
export let triggerSwitchEventSpy;
|
|
69
|
+
// Spy on PluginManager methods
|
|
40
70
|
export let installPluginSpy;
|
|
41
71
|
export let uninstallPluginSpy;
|
|
42
72
|
export let addPluginSpy;
|
|
@@ -47,12 +77,14 @@ export let shutdownPluginSpy;
|
|
|
47
77
|
export let removePluginSpy;
|
|
48
78
|
export let enablePluginSpy;
|
|
49
79
|
export let disablePluginSpy;
|
|
80
|
+
// Spy on Frontend methods
|
|
50
81
|
export let wssSendSnackbarMessageSpy;
|
|
51
82
|
export let wssSendCloseSnackbarMessageSpy;
|
|
52
83
|
export let wssSendUpdateRequiredSpy;
|
|
53
84
|
export let wssSendRefreshRequiredSpy;
|
|
54
85
|
export let wssSendRestartRequiredSpy;
|
|
55
86
|
export let wssSendRestartNotRequiredSpy;
|
|
87
|
+
// Spy on BroadcastServer methods
|
|
56
88
|
export let broadcastServerIsWorkerRequestSpy;
|
|
57
89
|
export let broadcastServerIsWorkerResponseSpy;
|
|
58
90
|
export let broadcastServerBroadcastSpy;
|
|
@@ -70,12 +102,29 @@ export let environment;
|
|
|
70
102
|
export let server;
|
|
71
103
|
export let aggregator;
|
|
72
104
|
export let log;
|
|
105
|
+
/**
|
|
106
|
+
* Setup the Jest environment:
|
|
107
|
+
* - it will remove any existing home directory
|
|
108
|
+
* - setup the spies for logging
|
|
109
|
+
*
|
|
110
|
+
* @param {string} name The name of the test suite.
|
|
111
|
+
* @param {boolean} debug If true, the logging is not mocked.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```typescript
|
|
115
|
+
* import { consoleDebugSpy, consoleErrorSpy, consoleInfoSpy, consoleLogSpy, consoleWarnSpy, loggerLogSpy, setDebug, setupTest } from './jestutils/jestHelpers.js';
|
|
116
|
+
*
|
|
117
|
+
* // Setup the test environment
|
|
118
|
+
* await setupTest(NAME, false);
|
|
119
|
+
* ```
|
|
120
|
+
*/
|
|
73
121
|
export async function setupTest(name, debug = false) {
|
|
74
122
|
expect(name).toBeDefined();
|
|
75
123
|
expect(typeof name).toBe('string');
|
|
76
124
|
expect(name.length).toBeGreaterThanOrEqual(4);
|
|
77
125
|
NAME = name;
|
|
78
126
|
HOMEDIR = path.join('jest', name);
|
|
127
|
+
// Cleanup any existing home directory
|
|
79
128
|
rmSync(HOMEDIR, { recursive: true, force: true });
|
|
80
129
|
const { jest } = await import('@jest/globals');
|
|
81
130
|
loggerDebugSpy = jest.spyOn(AnsiLogger.prototype, 'debug');
|
|
@@ -130,8 +179,26 @@ export async function setupTest(name, debug = false) {
|
|
|
130
179
|
broadcastServerRequestSpy = jest.spyOn(BroadcastServer.prototype, 'request');
|
|
131
180
|
broadcastServerRespondSpy = jest.spyOn(BroadcastServer.prototype, 'respond');
|
|
132
181
|
broadcastServerFetchSpy = jest.spyOn(BroadcastServer.prototype, 'fetch');
|
|
182
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
133
183
|
broadcastMessageHandlerSpy = jest.spyOn(BroadcastServer.prototype, 'broadcastMessageHandler');
|
|
134
184
|
}
|
|
185
|
+
/**
|
|
186
|
+
* Set or unset the debug mode.
|
|
187
|
+
*
|
|
188
|
+
* @param {boolean} debug If true, the logging is not mocked.
|
|
189
|
+
* @returns {Promise<void>} A promise that resolves when the debug mode is set.
|
|
190
|
+
*
|
|
191
|
+
* @example
|
|
192
|
+
* ```typescript
|
|
193
|
+
* // Set the debug mode in test environment
|
|
194
|
+
* await setDebug(true);
|
|
195
|
+
* ```
|
|
196
|
+
*
|
|
197
|
+
* ```typescript
|
|
198
|
+
* // Reset the debug mode in test environment
|
|
199
|
+
* await setDebug(false);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
135
202
|
export async function setDebug(debug) {
|
|
136
203
|
const { jest } = await import('@jest/globals');
|
|
137
204
|
if (debug) {
|
|
@@ -157,18 +224,41 @@ export async function setDebug(debug) {
|
|
|
157
224
|
consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
158
225
|
}
|
|
159
226
|
}
|
|
227
|
+
/**
|
|
228
|
+
* Create and start a fully initialized Matterbridge instance for testing.
|
|
229
|
+
*
|
|
230
|
+
* @param {('bridge' | 'childbridge' | 'controller' | '')} bridgeMode The bridge mode to start the Matterbridge instance in.
|
|
231
|
+
* @param {number} frontendPort The frontend port number.
|
|
232
|
+
* @param {number} matterPort The matter port number.
|
|
233
|
+
* @param {number} passcode The passcode number.
|
|
234
|
+
* @param {number} discriminator The discriminator number.
|
|
235
|
+
* @param {number} pluginSize The expected number of plugins.
|
|
236
|
+
* @param {number} devicesSize The expected number of devices.
|
|
237
|
+
* @returns {Promise<Matterbridge>} The Matterbridge instance.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```typescript
|
|
241
|
+
* // Create and start a fully initialized Matterbridge instance for testing.
|
|
242
|
+
* await startMatterbridge();
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
160
245
|
export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 8283, matterPort = 5540, passcode = 20252026, discriminator = 3840, pluginSize = 0, devicesSize = 0) {
|
|
246
|
+
// Set the environment variables
|
|
161
247
|
process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS'] = '100';
|
|
162
248
|
process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS'] = '100';
|
|
249
|
+
// Setup the process arguments
|
|
163
250
|
process.argv.length = 0;
|
|
164
251
|
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());
|
|
252
|
+
// Load Matterbridge instance and initialize it
|
|
165
253
|
matterbridge = await Matterbridge.loadInstance(true);
|
|
166
254
|
expect(matterbridge).toBeDefined();
|
|
167
255
|
expect(matterbridge.profile).toBeUndefined();
|
|
168
256
|
expect(matterbridge.bridgeMode).toBe(bridgeMode);
|
|
257
|
+
// Get the frontend, plugins and devices
|
|
169
258
|
frontend = matterbridge.frontend;
|
|
170
259
|
plugins = matterbridge.plugins;
|
|
171
260
|
devices = matterbridge.devices;
|
|
261
|
+
// @ts-expect-error - access to private member for testing
|
|
172
262
|
expect(matterbridge.initialized).toBeTruthy();
|
|
173
263
|
expect(matterbridge.log).toBeDefined();
|
|
174
264
|
expect(matterbridge.rootDirectory).toBe(path.resolve('./'));
|
|
@@ -181,10 +271,15 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
|
|
|
181
271
|
expect(devices).toBeDefined();
|
|
182
272
|
expect(devices.size).toBe(devicesSize);
|
|
183
273
|
expect(frontend).toBeDefined();
|
|
274
|
+
// @ts-expect-error - access to private member for testing
|
|
184
275
|
expect(frontend.listening).toBeTruthy();
|
|
276
|
+
// @ts-expect-error - access to private member for testing
|
|
185
277
|
expect(frontend.httpServer).toBeDefined();
|
|
278
|
+
// @ts-expect-error - access to private member for testing
|
|
186
279
|
expect(frontend.httpsServer).toBeUndefined();
|
|
280
|
+
// @ts-expect-error - access to private member for testing
|
|
187
281
|
expect(frontend.expressApp).toBeDefined();
|
|
282
|
+
// @ts-expect-error - access to private member for testing
|
|
188
283
|
expect(frontend.webSocketServer).toBeDefined();
|
|
189
284
|
expect(matterbridge.nodeStorage).toBeDefined();
|
|
190
285
|
expect(matterbridge.nodeContext).toBeDefined();
|
|
@@ -222,25 +317,51 @@ export async function startMatterbridge(bridgeMode = 'bridge', frontendPort = 82
|
|
|
222
317
|
});
|
|
223
318
|
});
|
|
224
319
|
}
|
|
225
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("info"
|
|
320
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("info" /* LogLevel.INFO */, `The frontend http server is listening on ${UNDERLINE}http://${matterbridge.systemInformation.ipv4Address}:${frontendPort}${UNDERLINEOFF}${rs}`);
|
|
226
321
|
if (bridgeMode === 'bridge') {
|
|
227
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
228
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
229
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
230
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
231
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
322
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Starting Matterbridge server node`);
|
|
323
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Server node for Matterbridge is online`);
|
|
324
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in bridge mode...`);
|
|
325
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in bridge mode`);
|
|
326
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge bridge started successfully`);
|
|
232
327
|
}
|
|
233
328
|
else if (bridgeMode === 'childbridge') {
|
|
234
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
235
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("debug"
|
|
236
|
-
expect(loggerLogSpy).toHaveBeenCalledWith("notice"
|
|
329
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Starting start matter interval in childbridge mode...`);
|
|
330
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("debug" /* LogLevel.DEBUG */, `Cleared startMatterInterval interval in childbridge mode`);
|
|
331
|
+
expect(loggerLogSpy).toHaveBeenCalledWith("notice" /* LogLevel.NOTICE */, `Matterbridge childbridge started successfully`);
|
|
237
332
|
}
|
|
238
333
|
return matterbridge;
|
|
239
334
|
}
|
|
335
|
+
/**
|
|
336
|
+
* Stop the fully initialized Matterbridge instance.
|
|
337
|
+
*
|
|
338
|
+
* @param {cleanupPause} cleanupPause The pause duration before cleanup. Default is 10 ms.
|
|
339
|
+
* @param {destroyPause} destroyPause The pause duration before destruction. Default is 250 ms.
|
|
340
|
+
*
|
|
341
|
+
* @example
|
|
342
|
+
* ```typescript
|
|
343
|
+
* // Stop the fully initialized Matterbridge instance.
|
|
344
|
+
* await stopMatterbridge();
|
|
345
|
+
* ```
|
|
346
|
+
*/
|
|
240
347
|
export async function stopMatterbridge(cleanupPause = 10, destroyPause = 250) {
|
|
241
348
|
await destroyMatterbridgeEnvironment(cleanupPause, destroyPause);
|
|
242
349
|
}
|
|
350
|
+
/**
|
|
351
|
+
* Create a Matterbridge instance for testing without initializing it.
|
|
352
|
+
*
|
|
353
|
+
* @param {string} name - Name for the environment (jest/name).
|
|
354
|
+
* @returns {Promise<Matterbridge>} The Matterbridge instance.
|
|
355
|
+
*
|
|
356
|
+
* @example
|
|
357
|
+
* ```typescript
|
|
358
|
+
* // Create Matterbridge environment
|
|
359
|
+
* await createMatterbridgeEnvironment(NAME);
|
|
360
|
+
* await startMatterbridgeEnvironment(MATTER_PORT);
|
|
361
|
+
* ```
|
|
362
|
+
*/
|
|
243
363
|
export async function createMatterbridgeEnvironment(name) {
|
|
364
|
+
// Create a MatterbridgeEdge instance
|
|
244
365
|
matterbridge = await Matterbridge.loadInstance(false);
|
|
245
366
|
expect(matterbridge).toBeDefined();
|
|
246
367
|
expect(matterbridge).toBeInstanceOf(Matterbridge);
|
|
@@ -251,28 +372,52 @@ export async function createMatterbridgeEnvironment(name) {
|
|
|
251
372
|
matterbridge.matterbridgeDirectory = path.join('jest', name, '.matterbridge');
|
|
252
373
|
matterbridge.matterbridgePluginDirectory = path.join('jest', name, 'Matterbridge');
|
|
253
374
|
matterbridge.matterbridgeCertDirectory = path.join('jest', name, '.mattercert');
|
|
254
|
-
matterbridge.log.logLevel = "debug"
|
|
255
|
-
log = new AnsiLogger({ logName: name, logTimestampFormat: 4
|
|
375
|
+
matterbridge.log.logLevel = "debug" /* LogLevel.DEBUG */;
|
|
376
|
+
log = new AnsiLogger({ logName: name, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
377
|
+
// Get the frontend, plugins and devices
|
|
256
378
|
frontend = matterbridge.frontend;
|
|
257
379
|
plugins = matterbridge.plugins;
|
|
258
380
|
devices = matterbridge.devices;
|
|
381
|
+
// Setup matter environment
|
|
382
|
+
// @ts-expect-error - access to private member for testing
|
|
259
383
|
matterbridge.environment = createTestEnvironment(name);
|
|
384
|
+
// @ts-expect-error - access to private member for testing
|
|
260
385
|
expect(matterbridge.environment).toBeDefined();
|
|
386
|
+
// @ts-expect-error - access to private member for testing
|
|
261
387
|
expect(matterbridge.environment).toBeInstanceOf(Environment);
|
|
262
388
|
return matterbridge;
|
|
263
389
|
}
|
|
390
|
+
/**
|
|
391
|
+
* Start the matterbridge environment.
|
|
392
|
+
* Only node storage, matter storage and the server and aggregator nodes are started.
|
|
393
|
+
*
|
|
394
|
+
* @param {number} port The matter server port.
|
|
395
|
+
* @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} The started server and aggregator.
|
|
396
|
+
*
|
|
397
|
+
* @example
|
|
398
|
+
* ```typescript
|
|
399
|
+
* // Create Matterbridge environment
|
|
400
|
+
* await createMatterbridgeEnvironment(NAME);
|
|
401
|
+
* await startMatterbridgeEnvironment(MATTER_PORT);
|
|
402
|
+
* ```
|
|
403
|
+
*/
|
|
264
404
|
export async function startMatterbridgeEnvironment(port = 5540) {
|
|
405
|
+
// Create the node storage
|
|
265
406
|
matterbridge.nodeStorage = new NodeStorageManager({ dir: path.join(matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
266
407
|
matterbridge.nodeContext = await matterbridge.nodeStorage.createStorage('matterbridge');
|
|
408
|
+
// Create the matter storage
|
|
409
|
+
// @ts-expect-error - access to private member for testing
|
|
267
410
|
await matterbridge.startMatterStorage();
|
|
268
411
|
expect(matterbridge.matterStorageService).toBeDefined();
|
|
269
412
|
expect(matterbridge.matterStorageManager).toBeDefined();
|
|
270
413
|
expect(matterbridge.matterbridgeContext).toBeDefined();
|
|
414
|
+
// @ts-expect-error - access to private member for testing
|
|
271
415
|
server = await matterbridge.createServerNode(matterbridge.matterbridgeContext, port);
|
|
272
416
|
expect(server).toBeDefined();
|
|
273
417
|
expect(server).toBeDefined();
|
|
274
418
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
275
419
|
matterbridge.serverNode = server;
|
|
420
|
+
// @ts-expect-error - access to private member for testing
|
|
276
421
|
aggregator = await matterbridge.createAggregatorNode(matterbridge.matterbridgeContext);
|
|
277
422
|
expect(aggregator).toBeDefined();
|
|
278
423
|
matterbridge.aggregatorNode = aggregator;
|
|
@@ -280,6 +425,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
280
425
|
expect(server.parts.has(aggregator.id)).toBeTruthy();
|
|
281
426
|
expect(server.parts.has(aggregator)).toBeTruthy();
|
|
282
427
|
expect(aggregator.lifecycle.isReady).toBeTruthy();
|
|
428
|
+
// Wait for the server to be online
|
|
283
429
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
284
430
|
await new Promise((resolve) => {
|
|
285
431
|
server.lifecycle.online.on(async () => {
|
|
@@ -287,6 +433,7 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
287
433
|
});
|
|
288
434
|
server.start();
|
|
289
435
|
});
|
|
436
|
+
// Check if the server is online
|
|
290
437
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
291
438
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
292
439
|
expect(server.lifecycle.isCommissioned).toBeFalsy();
|
|
@@ -298,11 +445,27 @@ export async function startMatterbridgeEnvironment(port = 5540) {
|
|
|
298
445
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
299
446
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
300
447
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
448
|
+
// Ensure the queue is empty and pause
|
|
301
449
|
await flushAsync();
|
|
302
450
|
return [server, aggregator];
|
|
303
451
|
}
|
|
452
|
+
/**
|
|
453
|
+
* Add a matterbridge platform for testing.
|
|
454
|
+
*
|
|
455
|
+
* @param {MatterbridgePlatform} platform The platform to add.
|
|
456
|
+
* @param {string} [name] Optional name of the platform.
|
|
457
|
+
*
|
|
458
|
+
* @example
|
|
459
|
+
* ```typescript
|
|
460
|
+
* platform = new Platform(matterbridge, log, config);
|
|
461
|
+
* // Add the platform to the Matterbridge environment
|
|
462
|
+
* addMatterbridgePlatform(platform);
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
304
465
|
export function addMatterbridgePlatform(platform, name) {
|
|
305
466
|
expect(platform).toBeDefined();
|
|
467
|
+
// Setup the platform MatterNode helpers
|
|
468
|
+
// @ts-expect-error - setMatterNode is intentionally private
|
|
306
469
|
platform.setMatterNode?.(matterbridge.addBridgedEndpoint.bind(matterbridge), matterbridge.removeBridgedEndpoint.bind(matterbridge), matterbridge.removeAllBridgedEndpoints.bind(matterbridge), matterbridge.addVirtualEndpoint.bind(matterbridge));
|
|
307
470
|
if (name)
|
|
308
471
|
platform.config.name = name;
|
|
@@ -313,6 +476,7 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
313
476
|
expect(platform.version).toBeDefined();
|
|
314
477
|
expect(platform.config.debug).toBeDefined();
|
|
315
478
|
expect(platform.config.unregisterOnShutdown).toBeDefined();
|
|
479
|
+
// @ts-expect-error accessing private member for testing
|
|
316
480
|
matterbridge.plugins._plugins.set(platform.config.name, {
|
|
317
481
|
name: platform.config.name,
|
|
318
482
|
path: './',
|
|
@@ -324,66 +488,152 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
324
488
|
});
|
|
325
489
|
platform['name'] = platform.config.name;
|
|
326
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* Stop the matterbridge environment
|
|
493
|
+
*
|
|
494
|
+
* @example
|
|
495
|
+
* ```typescript
|
|
496
|
+
* // Destroy Matterbridge environment
|
|
497
|
+
* await stopMatterbridgeEnvironment();
|
|
498
|
+
* await destroyMatterbridgeEnvironment();
|
|
499
|
+
* ```
|
|
500
|
+
*/
|
|
327
501
|
export async function stopMatterbridgeEnvironment() {
|
|
328
502
|
expect(matterbridge).toBeDefined();
|
|
329
503
|
expect(server).toBeDefined();
|
|
330
504
|
expect(aggregator).toBeDefined();
|
|
505
|
+
// Flush any pending endpoint number persistence
|
|
331
506
|
await flushAllEndpointNumberPersistence(server);
|
|
507
|
+
// Ensure all endpoint numbers are persisted
|
|
332
508
|
await assertAllEndpointNumbersPersisted(server);
|
|
509
|
+
// Close the server node
|
|
333
510
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
334
511
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
335
512
|
await server.close();
|
|
336
513
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
337
514
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
515
|
+
// Stop the matter storage
|
|
516
|
+
// @ts-expect-error - access to private member for testing
|
|
338
517
|
await matterbridge.stopMatterStorage();
|
|
339
518
|
expect(matterbridge.matterStorageService).not.toBeDefined();
|
|
340
519
|
expect(matterbridge.matterStorageManager).not.toBeDefined();
|
|
341
520
|
expect(matterbridge.matterbridgeContext).not.toBeDefined();
|
|
521
|
+
// Stop the node storage
|
|
342
522
|
await matterbridge.nodeContext?.close();
|
|
343
523
|
matterbridge.nodeContext = undefined;
|
|
344
524
|
await matterbridge.nodeStorage?.close();
|
|
345
525
|
matterbridge.nodeStorage = undefined;
|
|
526
|
+
// Ensure the queue is empty and pause
|
|
346
527
|
await flushAsync();
|
|
347
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Destroy the matterbridge environment
|
|
531
|
+
*
|
|
532
|
+
* @param {number} cleanupPause The timeout for the destroy operation (default 10ms).
|
|
533
|
+
* @param {number} destroyPause The pause duration after cleanup before destroying the instance (default 250ms).
|
|
534
|
+
*
|
|
535
|
+
* @example
|
|
536
|
+
* ```typescript
|
|
537
|
+
* // Destroy Matterbridge environment
|
|
538
|
+
* await stopMatterbridgeEnvironment();
|
|
539
|
+
* await destroyMatterbridgeEnvironment();
|
|
540
|
+
* ```
|
|
541
|
+
*/
|
|
348
542
|
export async function destroyMatterbridgeEnvironment(cleanupPause = 10, destroyPause = 250) {
|
|
543
|
+
// Destroy a matterbridge instance
|
|
349
544
|
await destroyInstance(matterbridge, cleanupPause, destroyPause);
|
|
545
|
+
// Close the mDNS service
|
|
350
546
|
await closeMdnsInstance(matterbridge);
|
|
547
|
+
// Reset the singleton instance
|
|
548
|
+
// @ts-expect-error - accessing private member for testing
|
|
351
549
|
Matterbridge.instance = undefined;
|
|
352
550
|
}
|
|
551
|
+
/**
|
|
552
|
+
* Destroy a matterbridge instance
|
|
553
|
+
*
|
|
554
|
+
* @param {Matterbridge} matterbridge The matterbridge instance to destroy.
|
|
555
|
+
* @param {number} cleanupPause The pause duration to wait for the cleanup to complete in milliseconds (default 10ms).
|
|
556
|
+
* @param {number} destroyPause The pause duration to wait after cleanup before destroying the instance in milliseconds (default 250ms).
|
|
557
|
+
*/
|
|
353
558
|
export async function destroyInstance(matterbridge, cleanupPause = 10, destroyPause = 250) {
|
|
559
|
+
// Cleanup the Matterbridge instance
|
|
560
|
+
// @ts-expect-error - accessing private member for testing
|
|
354
561
|
await matterbridge.cleanup('destroying instance...', false, cleanupPause);
|
|
562
|
+
// Pause before destroying the instance
|
|
355
563
|
if (destroyPause > 0)
|
|
356
564
|
await flushAsync(undefined, undefined, destroyPause);
|
|
357
565
|
}
|
|
566
|
+
/**
|
|
567
|
+
* Close the mDNS instance in the matterbridge environment.
|
|
568
|
+
*
|
|
569
|
+
* @param {Matterbridge} matterbridge The matterbridge instance.
|
|
570
|
+
* @returns {Promise<void>} A promise that resolves when the mDNS instance is closed.
|
|
571
|
+
*/
|
|
358
572
|
export async function closeMdnsInstance(matterbridge) {
|
|
573
|
+
// TODO: matter.js 0.16.0 - provide close method to close the mDNS service
|
|
574
|
+
// @ts-expect-error - accessing private member for testing
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
359
576
|
const mdns = matterbridge.environment.get(MdnsService);
|
|
360
577
|
if (mdns && mdns[Symbol.asyncDispose] && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
361
578
|
await mdns[Symbol.asyncDispose]();
|
|
362
579
|
if (mdns && mdns.close && typeof mdns.close === 'function')
|
|
363
580
|
await mdns.close();
|
|
364
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Create a matter test environment for testing:
|
|
584
|
+
* - it will remove any existing home directory
|
|
585
|
+
* - setup the matter environment with name, debug logging and ANSI format
|
|
586
|
+
* - setup the mDNS service in the environment
|
|
587
|
+
*
|
|
588
|
+
* @param {string} name - Name for the environment (jest/name).
|
|
589
|
+
* @returns {Environment} - The default matter environment.
|
|
590
|
+
*/
|
|
365
591
|
export function createTestEnvironment(name) {
|
|
366
592
|
expect(name).toBeDefined();
|
|
367
593
|
expect(typeof name).toBe('string');
|
|
368
|
-
expect(name.length).toBeGreaterThanOrEqual(4);
|
|
369
|
-
|
|
594
|
+
expect(name.length).toBeGreaterThanOrEqual(4); // avoid accidental deletion of short paths like "/" or "C:\"
|
|
595
|
+
// Setup the logger
|
|
596
|
+
log = new AnsiLogger({ logName: name, logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
597
|
+
// Cleanup any existing home directory
|
|
370
598
|
rmSync(path.join('jest', name), { recursive: true, force: true });
|
|
599
|
+
// Setup the matter environment
|
|
371
600
|
environment = Environment.default;
|
|
372
601
|
environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
373
602
|
environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
374
603
|
environment.vars.set('path.root', path.join('jest', name, '.matterbridge', MATTER_STORAGE_NAME));
|
|
375
604
|
environment.vars.set('runtime.signals', false);
|
|
376
605
|
environment.vars.set('runtime.exitcode', false);
|
|
606
|
+
// Setup the mDNS service in the environment
|
|
377
607
|
new MdnsService(environment);
|
|
608
|
+
// await environment.get(MdnsService)?.construction.ready;
|
|
378
609
|
return environment;
|
|
379
610
|
}
|
|
611
|
+
/**
|
|
612
|
+
* Destroy the matter test environment by closing the mDNS service.
|
|
613
|
+
*
|
|
614
|
+
* @returns {Promise<void>} A promise that resolves when the test environment is destroyed.
|
|
615
|
+
*/
|
|
380
616
|
export async function destroyTestEnvironment() {
|
|
617
|
+
// stop the mDNS service
|
|
618
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
619
|
const mdns = environment.get(MdnsService);
|
|
382
620
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
383
621
|
await mdns[Symbol.asyncDispose]();
|
|
384
622
|
if (mdns && typeof mdns.close === 'function')
|
|
385
623
|
await mdns.close();
|
|
386
624
|
}
|
|
625
|
+
/**
|
|
626
|
+
* Advance the Node.js event loop deterministically to allow chained asynchronous work (Promises scheduled in
|
|
627
|
+
* microtasks and follow‑up macrotasks) to complete inside tests without adding arbitrary long timeouts.
|
|
628
|
+
*
|
|
629
|
+
* NOTE: This does not guarantee OS level network IO completion—only JavaScript task queue progression inside the
|
|
630
|
+
* current process.
|
|
631
|
+
*
|
|
632
|
+
* @param {number} ticks Number of macrotask (setImmediate) turns to yield (default 3).
|
|
633
|
+
* @param {number} microTurns Number of microtask drains (Promise.resolve chains) after macrotask yielding (default 10).
|
|
634
|
+
* @param {number} pause Final timer delay in ms; set 0 to disable (default 250ms).
|
|
635
|
+
* @returns {Promise<void>} Resolves after the requested event loop advancement has completed.
|
|
636
|
+
*/
|
|
387
637
|
export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
388
638
|
for (let i = 0; i < ticks; i++)
|
|
389
639
|
await new Promise((resolve) => setImmediate(resolve));
|
|
@@ -392,16 +642,33 @@ export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
|
392
642
|
if (pause)
|
|
393
643
|
await new Promise((resolve) => setTimeout(resolve, pause));
|
|
394
644
|
}
|
|
645
|
+
/**
|
|
646
|
+
* Summarize live libuv handles/requests inside a process.
|
|
647
|
+
*
|
|
648
|
+
* @param {AnsiLogger} log - Logger to use for output
|
|
649
|
+
*
|
|
650
|
+
* @returns {number} - The total number of active handles and requests
|
|
651
|
+
*/
|
|
395
652
|
export function logKeepAlives(log) {
|
|
653
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
396
654
|
const handles = process._getActiveHandles?.() ?? [];
|
|
655
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
397
656
|
const requests = process._getActiveRequests?.() ?? [];
|
|
657
|
+
// istanbul ignore next
|
|
398
658
|
const fmtHandle = (h, i) => {
|
|
399
659
|
const ctor = h?.constructor?.name ?? 'Unknown';
|
|
660
|
+
// Timer-like?
|
|
661
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
400
662
|
const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : undefined;
|
|
663
|
+
// MessagePort?
|
|
664
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
401
665
|
const isPort = h?.constructor?.name?.includes('MessagePort');
|
|
666
|
+
// Socket/Server?
|
|
667
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
402
668
|
const fd = h?.fd ?? h?._handle?.fd;
|
|
403
669
|
return { i, type: ctor, hasRef, isPort, fd };
|
|
404
670
|
};
|
|
671
|
+
// istanbul ignore next
|
|
405
672
|
const fmtReq = (r, i) => {
|
|
406
673
|
const ctor = r?.constructor?.name ?? 'Unknown';
|
|
407
674
|
return { i, type: ctor };
|
|
@@ -410,6 +677,7 @@ export function logKeepAlives(log) {
|
|
|
410
677
|
handles: handles.map(fmtHandle),
|
|
411
678
|
requests: requests.map(fmtReq),
|
|
412
679
|
};
|
|
680
|
+
// istanbul ignore next if
|
|
413
681
|
if (summary.handles.length === 0 && summary.requests.length === 0) {
|
|
414
682
|
log?.debug('KeepAlive: no active handles or requests.');
|
|
415
683
|
}
|
|
@@ -421,6 +689,19 @@ export function logKeepAlives(log) {
|
|
|
421
689
|
}
|
|
422
690
|
return summary.handles.length + summary.requests.length;
|
|
423
691
|
}
|
|
692
|
+
/**
|
|
693
|
+
* Flush (await) the lazy endpoint number persistence mechanism used by matter.js.
|
|
694
|
+
*
|
|
695
|
+
* Background:
|
|
696
|
+
* assignNumber() batches persistence (store.saveNumber + updating __nextNumber__) via an internal promise (#numbersPersisted).
|
|
697
|
+
* Calling endpointStores.close() waits for the current batch only. If new endpoints were added in the same macrotask
|
|
698
|
+
* cycle additional micro/macro turns might be needed to ensure the batch started. We defensively yield macrotasks
|
|
699
|
+
* (setImmediate) and then await close() multiple rounds.
|
|
700
|
+
*
|
|
701
|
+
* @param {ServerNode} targetServer The server whose endpoint numbering persistence should be flushed.
|
|
702
|
+
* @param {number} rounds Number of macrotask + close cycles to run (2 is usually sufficient; 1 often works).
|
|
703
|
+
* @returns {Promise<void>} Resolves when pending number persistence batches have completed.
|
|
704
|
+
*/
|
|
424
705
|
export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
|
|
425
706
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
426
707
|
for (let i = 0; i < rounds; i++) {
|
|
@@ -428,6 +709,12 @@ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2
|
|
|
428
709
|
await nodeStore.endpointStores.close();
|
|
429
710
|
}
|
|
430
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Collect all endpoints in the server endpoint tree (root -> descendants).
|
|
714
|
+
*
|
|
715
|
+
* @param {Endpoint} root Root endpoint (typically the ServerNode root endpoint cast as Endpoint).
|
|
716
|
+
* @returns {Endpoint[]} Flat array including the root and every descendant once.
|
|
717
|
+
*/
|
|
431
718
|
function collectAllEndpoints(root) {
|
|
432
719
|
const list = [];
|
|
433
720
|
const walk = (ep) => {
|
|
@@ -441,14 +728,26 @@ function collectAllEndpoints(root) {
|
|
|
441
728
|
walk(root);
|
|
442
729
|
return list;
|
|
443
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* Assert that every endpoint attached to the server has an assigned and (batch-)persisted endpoint number.
|
|
733
|
+
*
|
|
734
|
+
* This waits for any outstanding number persistence batch (endpointStores.close()), then traverses the endpoint
|
|
735
|
+
* graph and asserts:
|
|
736
|
+
* - Root endpoint: number is 0 (allowing undefined to coerce to 0 via nullish coalescing check).
|
|
737
|
+
* - All other endpoints: number > 0.
|
|
738
|
+
*
|
|
739
|
+
* @param {ServerNode} targetServer The server whose endpoint numbers are verified.
|
|
740
|
+
* @returns {Promise<void>} Resolves when assertions complete.
|
|
741
|
+
*/
|
|
444
742
|
export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
445
743
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
744
|
+
// Ensure any pending persistence finished (flush any in-flight batch promise)
|
|
446
745
|
await nodeStore.endpointStores.close();
|
|
447
746
|
const all = collectAllEndpoints(targetServer);
|
|
448
747
|
for (const ep of all) {
|
|
449
748
|
const store = nodeStore.storeForEndpoint(ep);
|
|
450
749
|
if (ep.maybeNumber === 0) {
|
|
451
|
-
expect(store.number ?? 0).toBe(0);
|
|
750
|
+
expect(store.number ?? 0).toBe(0); // root
|
|
452
751
|
}
|
|
453
752
|
else {
|
|
454
753
|
expect(store.number).toBeGreaterThan(0);
|
|
@@ -456,23 +755,42 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
|
456
755
|
}
|
|
457
756
|
return all.length;
|
|
458
757
|
}
|
|
758
|
+
/**
|
|
759
|
+
* Close the server node stores to flush any pending endpoint number persistence.
|
|
760
|
+
*
|
|
761
|
+
* @param {ServerNode} targetServer The server whose endpoint stores should be closed.
|
|
762
|
+
* @returns {Promise<void>} Resolves when the stores have been closed.
|
|
763
|
+
*/
|
|
459
764
|
export async function closeServerNodeStores(targetServer) {
|
|
765
|
+
// Close endpoint stores to avoid number persistence issues
|
|
460
766
|
if (!targetServer)
|
|
461
767
|
targetServer = server;
|
|
462
768
|
await targetServer?.env.get(ServerNodeStore)?.endpointStores.close();
|
|
463
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* Start a matter server node for testing.
|
|
772
|
+
*
|
|
773
|
+
* @param {string} name Name of the server (used for logging and product description).
|
|
774
|
+
* @param {number} port TCP port to listen on.
|
|
775
|
+
* @param {DeviceTypeId} deviceType Device type identifier for the server node.
|
|
776
|
+
* @returns {Promise<[ServerNode<ServerNode.RootEndpoint>, Endpoint<AggregatorEndpoint>]>} Resolves to an array containing the created ServerNode and its AggregatorNode.
|
|
777
|
+
*/
|
|
464
778
|
export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
465
779
|
const { randomBytes } = await import('node:crypto');
|
|
466
780
|
const random = randomBytes(8).toString('hex');
|
|
781
|
+
// Create the server node
|
|
467
782
|
server = await ServerNode.create({
|
|
468
783
|
id: name + 'ServerNode',
|
|
784
|
+
// Provide the environment
|
|
469
785
|
environment,
|
|
786
|
+
// Provide Node announcement settings
|
|
470
787
|
productDescription: {
|
|
471
788
|
name: name + 'ServerNode',
|
|
472
789
|
deviceType: DeviceTypeId(deviceType),
|
|
473
790
|
vendorId: VendorId(0xfff1),
|
|
474
791
|
productId: 0x8000,
|
|
475
792
|
},
|
|
793
|
+
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
476
794
|
basicInformation: {
|
|
477
795
|
vendorId: VendorId(0xfff1),
|
|
478
796
|
vendorName: 'Matterbridge',
|
|
@@ -485,32 +803,39 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
485
803
|
serialNumber: 'SN' + random,
|
|
486
804
|
uniqueId: 'UI' + random,
|
|
487
805
|
},
|
|
806
|
+
// Provide Network relevant configuration like the port
|
|
488
807
|
network: {
|
|
489
808
|
listeningAddressIpv4: undefined,
|
|
490
809
|
listeningAddressIpv6: undefined,
|
|
491
810
|
port,
|
|
492
811
|
},
|
|
812
|
+
// Provide the certificate for the device
|
|
493
813
|
operationalCredentials: {
|
|
494
814
|
certification: undefined,
|
|
495
815
|
},
|
|
496
816
|
});
|
|
497
817
|
expect(server).toBeDefined();
|
|
498
818
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
819
|
+
// Create the aggregator node
|
|
499
820
|
aggregator = new Endpoint(AggregatorEndpoint, {
|
|
500
821
|
id: name + 'AggregatorNode',
|
|
501
822
|
});
|
|
502
823
|
expect(aggregator).toBeDefined();
|
|
824
|
+
// Add the aggregator to the server
|
|
503
825
|
await server.add(aggregator);
|
|
504
826
|
expect(server.parts.has(aggregator.id)).toBeTruthy();
|
|
505
827
|
expect(server.parts.has(aggregator)).toBeTruthy();
|
|
506
828
|
expect(aggregator.lifecycle.isReady).toBeTruthy();
|
|
829
|
+
// Run the server
|
|
507
830
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
831
|
+
// Wait for the server to be online
|
|
508
832
|
await new Promise((resolve) => {
|
|
509
833
|
server.lifecycle.online.on(async () => {
|
|
510
834
|
resolve();
|
|
511
835
|
});
|
|
512
836
|
server.start();
|
|
513
837
|
});
|
|
838
|
+
// Check if the server is online
|
|
514
839
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
515
840
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
516
841
|
expect(server.lifecycle.isCommissioned).toBeFalsy();
|
|
@@ -522,31 +847,53 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
522
847
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
523
848
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
524
849
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
850
|
+
// Ensure the queue is empty and pause 250ms
|
|
525
851
|
await flushAsync();
|
|
526
852
|
return [server, aggregator];
|
|
527
853
|
}
|
|
854
|
+
/**
|
|
855
|
+
* Stop a matter server node.
|
|
856
|
+
*
|
|
857
|
+
* @param {ServerNode<ServerNode.RootEndpoint>} server The server to stop.
|
|
858
|
+
* @returns {Promise<void>} Resolves when the server has stopped.
|
|
859
|
+
*/
|
|
528
860
|
export async function stopServerNode(server) {
|
|
861
|
+
// Flush any pending endpoint number persistence
|
|
529
862
|
await flushAllEndpointNumberPersistence(server);
|
|
863
|
+
// Ensure all endpoint numbers are persisted
|
|
530
864
|
await assertAllEndpointNumbersPersisted(server);
|
|
865
|
+
// Stop the server
|
|
531
866
|
expect(server).toBeDefined();
|
|
532
867
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
533
868
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
534
869
|
await server.close();
|
|
535
870
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
536
871
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
872
|
+
// stop the mDNS service
|
|
873
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
537
874
|
const mdns = environment.get(MdnsService);
|
|
538
875
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
539
876
|
await mdns[Symbol.asyncDispose]();
|
|
540
877
|
if (mdns && typeof mdns.close === 'function')
|
|
541
878
|
await mdns.close();
|
|
879
|
+
// Ensure the queue is empty and pause 250ms
|
|
542
880
|
await flushAsync();
|
|
543
881
|
}
|
|
882
|
+
/**
|
|
883
|
+
* Add a device (endpoint) to a matter server node or an aggregator.
|
|
884
|
+
*
|
|
885
|
+
* @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to add the device to.
|
|
886
|
+
* @param {Endpoint} device The device to add.
|
|
887
|
+
* @param {number} pause The pause time in milliseconds after addition (default 10ms).
|
|
888
|
+
* @returns {Promise<void>} Resolves when the device has been added and is ready.
|
|
889
|
+
*/
|
|
544
890
|
export async function addDevice(owner, device, pause = 10) {
|
|
545
891
|
expect(owner).toBeDefined();
|
|
546
892
|
expect(device).toBeDefined();
|
|
547
893
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
548
894
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
549
895
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
896
|
+
// istanbul ignore next
|
|
550
897
|
try {
|
|
551
898
|
await owner.add(device);
|
|
552
899
|
}
|
|
@@ -566,12 +913,21 @@ export async function addDevice(owner, device, pause = 10) {
|
|
|
566
913
|
await flushAsync(undefined, undefined, pause);
|
|
567
914
|
return true;
|
|
568
915
|
}
|
|
916
|
+
/**
|
|
917
|
+
* Delete a device (endpoint) from a matter server node or an aggregator.
|
|
918
|
+
*
|
|
919
|
+
* @param {ServerNode<ServerNode.RootEndpoint> | Endpoint<AggregatorEndpoint>} owner The server or aggregator to remove the device from.
|
|
920
|
+
* @param {Endpoint} device The device to remove.
|
|
921
|
+
* @param {number} pause The pause time in milliseconds after deletion (default 10ms).
|
|
922
|
+
* @returns {Promise<void>} Resolves when the device has been removed and is no longer ready.
|
|
923
|
+
*/
|
|
569
924
|
export async function deleteDevice(owner, device, pause = 10) {
|
|
570
925
|
expect(owner).toBeDefined();
|
|
571
926
|
expect(device).toBeDefined();
|
|
572
927
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
573
928
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
574
929
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
930
|
+
// istanbul ignore next
|
|
575
931
|
try {
|
|
576
932
|
await device.delete();
|
|
577
933
|
}
|
|
@@ -591,3 +947,4 @@ export async function deleteDevice(owner, device, pause = 10) {
|
|
|
591
947
|
await flushAsync(undefined, undefined, pause);
|
|
592
948
|
return true;
|
|
593
949
|
}
|
|
950
|
+
//# sourceMappingURL=jestHelpers.js.map
|