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