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