matterbridge 3.4.1-dev-20251130-422c2fc → 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 -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 +340 -0
- package/dist/jestutils/jestHelpers.d.ts.map +1 -0
- package/dist/jestutils/jestHelpers.js +375 -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 +493 -0
- package/dist/matterbridge.d.ts.map +1 -0
- package/dist/matterbridge.js +808 -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 +471 -8
- 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 +342 -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,13 +435,30 @@ 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) {
|
|
456
|
+
expect(platform).toBeDefined();
|
|
457
|
+
// Setup the platform MatterNode helpers
|
|
458
|
+
// @ts-expect-error - setMatterNode is intentionally private
|
|
459
|
+
platform.setMatterNode?.(matterbridge.addBridgedEndpoint.bind(matterbridge), matterbridge.removeBridgedEndpoint.bind(matterbridge), matterbridge.removeAllBridgedEndpoints.bind(matterbridge), matterbridge.addVirtualEndpoint.bind(matterbridge));
|
|
293
460
|
if (name)
|
|
294
461
|
platform.config.name = name;
|
|
295
|
-
expect(platform).toBeDefined();
|
|
296
462
|
expect(platform.config.name).toBeDefined();
|
|
297
463
|
expect(platform.config.type).toBeDefined();
|
|
298
464
|
expect(platform.type).toBeDefined();
|
|
@@ -300,6 +466,7 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
300
466
|
expect(platform.version).toBeDefined();
|
|
301
467
|
expect(platform.config.debug).toBeDefined();
|
|
302
468
|
expect(platform.config.unregisterOnShutdown).toBeDefined();
|
|
469
|
+
// @ts-expect-error accessing private member for testing
|
|
303
470
|
matterbridge.plugins._plugins.set(platform.config.name, {
|
|
304
471
|
name: platform.config.name,
|
|
305
472
|
path: './',
|
|
@@ -311,63 +478,150 @@ export function addMatterbridgePlatform(platform, name) {
|
|
|
311
478
|
});
|
|
312
479
|
platform['name'] = platform.config.name;
|
|
313
480
|
}
|
|
481
|
+
/**
|
|
482
|
+
* Stop the matterbridge environment
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* // Destroy Matterbridge environment
|
|
487
|
+
* await stopMatterbridgeEnvironment();
|
|
488
|
+
* await destroyMatterbridgeEnvironment();
|
|
489
|
+
* ```
|
|
490
|
+
*/
|
|
314
491
|
export async function stopMatterbridgeEnvironment() {
|
|
315
492
|
expect(matterbridge).toBeDefined();
|
|
316
493
|
expect(server).toBeDefined();
|
|
317
494
|
expect(aggregator).toBeDefined();
|
|
495
|
+
// Flush any pending endpoint number persistence
|
|
318
496
|
await flushAllEndpointNumberPersistence(server);
|
|
497
|
+
// Ensure all endpoint numbers are persisted
|
|
319
498
|
await assertAllEndpointNumbersPersisted(server);
|
|
499
|
+
// Close the server node
|
|
320
500
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
321
501
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
322
502
|
await server.close();
|
|
323
503
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
324
504
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
505
|
+
// Stop the matter storage
|
|
506
|
+
// @ts-expect-error - access to private member for testing
|
|
325
507
|
await matterbridge.stopMatterStorage();
|
|
326
508
|
expect(matterbridge.matterStorageService).not.toBeDefined();
|
|
327
509
|
expect(matterbridge.matterStorageManager).not.toBeDefined();
|
|
328
510
|
expect(matterbridge.matterbridgeContext).not.toBeDefined();
|
|
511
|
+
// Stop the node storage
|
|
329
512
|
await matterbridge.nodeContext?.close();
|
|
513
|
+
matterbridge.nodeContext = undefined;
|
|
330
514
|
await matterbridge.nodeStorage?.close();
|
|
515
|
+
matterbridge.nodeStorage = undefined;
|
|
516
|
+
// Ensure the queue is empty and pause
|
|
331
517
|
await flushAsync();
|
|
332
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
|
+
*/
|
|
333
532
|
export async function destroyMatterbridgeEnvironment(cleanupPause = 10, destroyPause = 250) {
|
|
533
|
+
// Destroy a matterbridge instance
|
|
334
534
|
await destroyInstance(matterbridge, cleanupPause, destroyPause);
|
|
535
|
+
// Close the mDNS service
|
|
335
536
|
await closeMdnsInstance(matterbridge);
|
|
537
|
+
// Reset the singleton instance
|
|
538
|
+
// @ts-expect-error - accessing private member for testing
|
|
336
539
|
Matterbridge.instance = undefined;
|
|
337
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
|
+
*/
|
|
338
548
|
export async function destroyInstance(matterbridge, cleanupPause = 10, destroyPause = 250) {
|
|
549
|
+
// Cleanup the Matterbridge instance
|
|
550
|
+
// @ts-expect-error - accessing private member for testing
|
|
339
551
|
await matterbridge.cleanup('destroying instance...', false, cleanupPause);
|
|
552
|
+
// Pause before destroying the instance
|
|
340
553
|
if (destroyPause > 0)
|
|
341
554
|
await flushAsync(undefined, undefined, destroyPause);
|
|
342
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
|
+
*/
|
|
343
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
|
|
344
566
|
const mdns = matterbridge.environment.get(MdnsService);
|
|
345
567
|
if (mdns && mdns[Symbol.asyncDispose] && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
346
568
|
await mdns[Symbol.asyncDispose]();
|
|
347
569
|
if (mdns && mdns.close && typeof mdns.close === 'function')
|
|
348
570
|
await mdns.close();
|
|
349
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
|
+
*/
|
|
350
581
|
export function createTestEnvironment(name) {
|
|
351
582
|
expect(name).toBeDefined();
|
|
352
583
|
expect(typeof name).toBe('string');
|
|
353
|
-
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
|
|
354
586
|
rmSync(path.join('jest', name), { recursive: true, force: true });
|
|
587
|
+
// Setup the matter environment
|
|
355
588
|
environment = Environment.default;
|
|
356
589
|
environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
357
590
|
environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
358
591
|
environment.vars.set('path.root', path.join('jest', name, '.matterbridge', MATTER_STORAGE_NAME));
|
|
359
592
|
environment.vars.set('runtime.signals', false);
|
|
360
593
|
environment.vars.set('runtime.exitcode', false);
|
|
594
|
+
// Setup the mDNS service in the environment
|
|
361
595
|
new MdnsService(environment);
|
|
596
|
+
// await environment.get(MdnsService)?.construction.ready;
|
|
362
597
|
return environment;
|
|
363
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
|
+
*/
|
|
364
604
|
export async function destroyTestEnvironment() {
|
|
605
|
+
// stop the mDNS service
|
|
606
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
365
607
|
const mdns = environment.get(MdnsService);
|
|
366
608
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
367
609
|
await mdns[Symbol.asyncDispose]();
|
|
368
610
|
if (mdns && typeof mdns.close === 'function')
|
|
369
611
|
await mdns.close();
|
|
370
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
|
+
*/
|
|
371
625
|
export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
372
626
|
for (let i = 0; i < ticks; i++)
|
|
373
627
|
await new Promise((resolve) => setImmediate(resolve));
|
|
@@ -376,16 +630,33 @@ export async function flushAsync(ticks = 3, microTurns = 10, pause = 250) {
|
|
|
376
630
|
if (pause)
|
|
377
631
|
await new Promise((resolve) => setTimeout(resolve, pause));
|
|
378
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
|
+
*/
|
|
379
640
|
export function logKeepAlives(log) {
|
|
641
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
380
642
|
const handles = process._getActiveHandles?.() ?? [];
|
|
643
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
381
644
|
const requests = process._getActiveRequests?.() ?? [];
|
|
645
|
+
// istanbul ignore next
|
|
382
646
|
const fmtHandle = (h, i) => {
|
|
383
647
|
const ctor = h?.constructor?.name ?? 'Unknown';
|
|
648
|
+
// Timer-like?
|
|
649
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
384
650
|
const hasRef = typeof h?.hasRef === 'function' ? h.hasRef() : undefined;
|
|
651
|
+
// MessagePort?
|
|
652
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
385
653
|
const isPort = h?.constructor?.name?.includes('MessagePort');
|
|
654
|
+
// Socket/Server?
|
|
655
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
386
656
|
const fd = h?.fd ?? h?._handle?.fd;
|
|
387
657
|
return { i, type: ctor, hasRef, isPort, fd };
|
|
388
658
|
};
|
|
659
|
+
// istanbul ignore next
|
|
389
660
|
const fmtReq = (r, i) => {
|
|
390
661
|
const ctor = r?.constructor?.name ?? 'Unknown';
|
|
391
662
|
return { i, type: ctor };
|
|
@@ -394,6 +665,7 @@ export function logKeepAlives(log) {
|
|
|
394
665
|
handles: handles.map(fmtHandle),
|
|
395
666
|
requests: requests.map(fmtReq),
|
|
396
667
|
};
|
|
668
|
+
// istanbul ignore next if
|
|
397
669
|
if (summary.handles.length === 0 && summary.requests.length === 0) {
|
|
398
670
|
log?.debug('KeepAlive: no active handles or requests.');
|
|
399
671
|
}
|
|
@@ -405,6 +677,19 @@ export function logKeepAlives(log) {
|
|
|
405
677
|
}
|
|
406
678
|
return summary.handles.length + summary.requests.length;
|
|
407
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
|
+
*/
|
|
408
693
|
export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2) {
|
|
409
694
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
410
695
|
for (let i = 0; i < rounds; i++) {
|
|
@@ -412,6 +697,12 @@ export async function flushAllEndpointNumberPersistence(targetServer, rounds = 2
|
|
|
412
697
|
await nodeStore.endpointStores.close();
|
|
413
698
|
}
|
|
414
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
|
+
*/
|
|
415
706
|
function collectAllEndpoints(root) {
|
|
416
707
|
const list = [];
|
|
417
708
|
const walk = (ep) => {
|
|
@@ -425,14 +716,26 @@ function collectAllEndpoints(root) {
|
|
|
425
716
|
walk(root);
|
|
426
717
|
return list;
|
|
427
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
|
+
*/
|
|
428
730
|
export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
429
731
|
const nodeStore = targetServer.env.get(ServerNodeStore);
|
|
732
|
+
// Ensure any pending persistence finished (flush any in-flight batch promise)
|
|
430
733
|
await nodeStore.endpointStores.close();
|
|
431
734
|
const all = collectAllEndpoints(targetServer);
|
|
432
735
|
for (const ep of all) {
|
|
433
736
|
const store = nodeStore.storeForEndpoint(ep);
|
|
434
737
|
if (ep.maybeNumber === 0) {
|
|
435
|
-
expect(store.number ?? 0).toBe(0);
|
|
738
|
+
expect(store.number ?? 0).toBe(0); // root
|
|
436
739
|
}
|
|
437
740
|
else {
|
|
438
741
|
expect(store.number).toBeGreaterThan(0);
|
|
@@ -440,23 +743,42 @@ export async function assertAllEndpointNumbersPersisted(targetServer) {
|
|
|
440
743
|
}
|
|
441
744
|
return all.length;
|
|
442
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
|
+
*/
|
|
443
752
|
export async function closeServerNodeStores(targetServer) {
|
|
753
|
+
// Close endpoint stores to avoid number persistence issues
|
|
444
754
|
if (!targetServer)
|
|
445
755
|
targetServer = server;
|
|
446
756
|
await targetServer?.env.get(ServerNodeStore)?.endpointStores.close();
|
|
447
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
|
+
*/
|
|
448
766
|
export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
449
767
|
const { randomBytes } = await import('node:crypto');
|
|
450
768
|
const random = randomBytes(8).toString('hex');
|
|
769
|
+
// Create the server node
|
|
451
770
|
server = await ServerNode.create({
|
|
452
771
|
id: name + 'ServerNode',
|
|
772
|
+
// Provide the environment
|
|
453
773
|
environment,
|
|
774
|
+
// Provide Node announcement settings
|
|
454
775
|
productDescription: {
|
|
455
776
|
name: name + 'ServerNode',
|
|
456
777
|
deviceType: DeviceTypeId(deviceType),
|
|
457
778
|
vendorId: VendorId(0xfff1),
|
|
458
779
|
productId: 0x8000,
|
|
459
780
|
},
|
|
781
|
+
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
460
782
|
basicInformation: {
|
|
461
783
|
vendorId: VendorId(0xfff1),
|
|
462
784
|
vendorName: 'Matterbridge',
|
|
@@ -469,32 +791,39 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
469
791
|
serialNumber: 'SN' + random,
|
|
470
792
|
uniqueId: 'UI' + random,
|
|
471
793
|
},
|
|
794
|
+
// Provide Network relevant configuration like the port
|
|
472
795
|
network: {
|
|
473
796
|
listeningAddressIpv4: undefined,
|
|
474
797
|
listeningAddressIpv6: undefined,
|
|
475
798
|
port,
|
|
476
799
|
},
|
|
800
|
+
// Provide the certificate for the device
|
|
477
801
|
operationalCredentials: {
|
|
478
802
|
certification: undefined,
|
|
479
803
|
},
|
|
480
804
|
});
|
|
481
805
|
expect(server).toBeDefined();
|
|
482
806
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
807
|
+
// Create the aggregator node
|
|
483
808
|
aggregator = new Endpoint(AggregatorEndpoint, {
|
|
484
809
|
id: name + 'AggregatorNode',
|
|
485
810
|
});
|
|
486
811
|
expect(aggregator).toBeDefined();
|
|
812
|
+
// Add the aggregator to the server
|
|
487
813
|
await server.add(aggregator);
|
|
488
814
|
expect(server.parts.has(aggregator.id)).toBeTruthy();
|
|
489
815
|
expect(server.parts.has(aggregator)).toBeTruthy();
|
|
490
816
|
expect(aggregator.lifecycle.isReady).toBeTruthy();
|
|
817
|
+
// Run the server
|
|
491
818
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
819
|
+
// Wait for the server to be online
|
|
492
820
|
await new Promise((resolve) => {
|
|
493
821
|
server.lifecycle.online.on(async () => {
|
|
494
822
|
resolve();
|
|
495
823
|
});
|
|
496
824
|
server.start();
|
|
497
825
|
});
|
|
826
|
+
// Check if the server is online
|
|
498
827
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
499
828
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
500
829
|
expect(server.lifecycle.isCommissioned).toBeFalsy();
|
|
@@ -506,31 +835,53 @@ export async function startServerNode(name, port, deviceType = bridge.code) {
|
|
|
506
835
|
expect(aggregator.lifecycle.isPartsReady).toBeTruthy();
|
|
507
836
|
expect(aggregator.lifecycle.hasId).toBeTruthy();
|
|
508
837
|
expect(aggregator.lifecycle.hasNumber).toBeTruthy();
|
|
838
|
+
// Ensure the queue is empty and pause 250ms
|
|
509
839
|
await flushAsync();
|
|
510
840
|
return [server, aggregator];
|
|
511
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
|
+
*/
|
|
512
848
|
export async function stopServerNode(server) {
|
|
849
|
+
// Flush any pending endpoint number persistence
|
|
513
850
|
await flushAllEndpointNumberPersistence(server);
|
|
851
|
+
// Ensure all endpoint numbers are persisted
|
|
514
852
|
await assertAllEndpointNumbersPersisted(server);
|
|
853
|
+
// Stop the server
|
|
515
854
|
expect(server).toBeDefined();
|
|
516
855
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
517
856
|
expect(server.lifecycle.isOnline).toBeTruthy();
|
|
518
857
|
await server.close();
|
|
519
858
|
expect(server.lifecycle.isReady).toBeTruthy();
|
|
520
859
|
expect(server.lifecycle.isOnline).toBeFalsy();
|
|
860
|
+
// stop the mDNS service
|
|
861
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
521
862
|
const mdns = environment.get(MdnsService);
|
|
522
863
|
if (mdns && typeof mdns[Symbol.asyncDispose] === 'function')
|
|
523
864
|
await mdns[Symbol.asyncDispose]();
|
|
524
865
|
if (mdns && typeof mdns.close === 'function')
|
|
525
866
|
await mdns.close();
|
|
867
|
+
// Ensure the queue is empty and pause 250ms
|
|
526
868
|
await flushAsync();
|
|
527
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
|
+
*/
|
|
528
878
|
export async function addDevice(owner, device, pause = 10) {
|
|
529
879
|
expect(owner).toBeDefined();
|
|
530
880
|
expect(device).toBeDefined();
|
|
531
881
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
532
882
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
533
883
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
884
|
+
// istanbul ignore next
|
|
534
885
|
try {
|
|
535
886
|
await owner.add(device);
|
|
536
887
|
}
|
|
@@ -550,12 +901,21 @@ export async function addDevice(owner, device, pause = 10) {
|
|
|
550
901
|
await flushAsync(undefined, undefined, pause);
|
|
551
902
|
return true;
|
|
552
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
|
+
*/
|
|
553
912
|
export async function deleteDevice(owner, device, pause = 10) {
|
|
554
913
|
expect(owner).toBeDefined();
|
|
555
914
|
expect(device).toBeDefined();
|
|
556
915
|
expect(owner.lifecycle.isReady).toBeTruthy();
|
|
557
916
|
expect(owner.construction.status).toBe(Lifecycle.Status.Active);
|
|
558
917
|
expect(owner.lifecycle.isPartsReady).toBeTruthy();
|
|
918
|
+
// istanbul ignore next
|
|
559
919
|
try {
|
|
560
920
|
await device.delete();
|
|
561
921
|
}
|
|
@@ -575,3 +935,4 @@ export async function deleteDevice(owner, device, pause = 10) {
|
|
|
575
935
|
await flushAsync(undefined, undefined, pause);
|
|
576
936
|
return true;
|
|
577
937
|
}
|
|
938
|
+
//# sourceMappingURL=jestHelpers.js.map
|