matterbridge 3.4.3-dev-20251209-e6cb85f → 3.4.3

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