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