@iobroker/testing 5.0.0 → 5.0.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 +1 -1
- package/README.md +1 -1
- package/build/index.d.ts +1 -1
- package/build/lib/adapterTools.d.ts +7 -1
- package/build/lib/adapterTools.js +36 -21
- package/build/lib/executeCommand.d.ts +3 -2
- package/build/lib/executeCommand.js +30 -29
- package/build/lib/str2regex.js +4 -4
- package/build/tests/index.d.ts +9 -9
- package/build/tests/integration/index.d.ts +1 -1
- package/build/tests/integration/index.js +44 -39
- package/build/tests/integration/lib/adapterSetup.d.ts +1 -1
- package/build/tests/integration/lib/adapterSetup.js +50 -52
- package/build/tests/integration/lib/controllerSetup.d.ts +1 -10
- package/build/tests/integration/lib/controllerSetup.js +59 -56
- package/build/tests/integration/lib/dbConnection.d.ts +12 -12
- package/build/tests/integration/lib/dbConnection.js +71 -69
- package/build/tests/integration/lib/harness.d.ts +9 -7
- package/build/tests/integration/lib/harness.js +68 -49
- package/build/tests/integration/lib/logger.js +1 -1
- package/build/tests/integration/lib/tools.d.ts +6 -1
- package/build/tests/integration/lib/tools.js +27 -12
- package/build/tests/packageFiles/index.js +92 -90
- package/build/tests/unit/harness/createMocks.d.ts +3 -2
- package/build/tests/unit/harness/createMocks.js +0 -1
- package/build/tests/unit/index.js +5 -4
- package/build/tests/unit/mocks/mockAdapter.d.ts +3 -3
- package/build/tests/unit/mocks/mockAdapter.js +195 -162
- package/build/tests/unit/mocks/mockAdapterCore.d.ts +4 -9
- package/build/tests/unit/mocks/mockAdapterCore.js +23 -14
- package/build/tests/unit/mocks/mockDatabase.d.ts +13 -12
- package/build/tests/unit/mocks/mockDatabase.js +40 -45
- package/build/tests/unit/mocks/mockLogger.d.ts +1 -1
- package/build/tests/unit/mocks/mockLogger.js +1 -1
- package/build/tests/unit/mocks/tools.d.ts +2 -2
- package/build/tests/unit/mocks/tools.js +14 -11
- package/package.json +15 -22
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -32,7 +42,7 @@ const events_1 = __importDefault(require("events"));
|
|
|
32
42
|
const fs_extra_1 = require("fs-extra");
|
|
33
43
|
const path = __importStar(require("path"));
|
|
34
44
|
const tools_1 = require("./tools");
|
|
35
|
-
const debug = (0, debug_1.default)(
|
|
45
|
+
const debug = (0, debug_1.default)('testing:integration:DBConnection');
|
|
36
46
|
/** The DB connection capsules access to the states and objects DB */
|
|
37
47
|
class DBConnection extends events_1.default {
|
|
38
48
|
/**
|
|
@@ -45,45 +55,45 @@ class DBConnection extends events_1.default {
|
|
|
45
55
|
this.testDir = testDir;
|
|
46
56
|
this.logger = logger;
|
|
47
57
|
this._isRunning = false;
|
|
48
|
-
this.getObject =
|
|
58
|
+
this.getObject = id => {
|
|
49
59
|
if (!this._objectsClient) {
|
|
50
|
-
throw new Error(
|
|
60
|
+
throw new Error('Objects DB is not running');
|
|
51
61
|
}
|
|
52
62
|
return this._objectsClient.getObjectAsync(id);
|
|
53
63
|
};
|
|
54
|
-
this.setObject =
|
|
64
|
+
this.setObject = (...args) => {
|
|
55
65
|
if (!this._objectsClient) {
|
|
56
|
-
throw new Error(
|
|
66
|
+
throw new Error('Objects DB is not running');
|
|
57
67
|
}
|
|
58
68
|
return this._objectsClient.setObjectAsync(...args);
|
|
59
69
|
};
|
|
60
|
-
this.delObject =
|
|
70
|
+
this.delObject = (...args) => {
|
|
61
71
|
if (!this._objectsClient) {
|
|
62
|
-
throw new Error(
|
|
72
|
+
throw new Error('Objects DB is not running');
|
|
63
73
|
}
|
|
64
74
|
return this._objectsClient.delObjectAsync(...args);
|
|
65
75
|
};
|
|
66
|
-
this.getState =
|
|
76
|
+
this.getState = (id) => {
|
|
67
77
|
if (!this._statesClient) {
|
|
68
|
-
throw new Error(
|
|
78
|
+
throw new Error('States DB is not running');
|
|
69
79
|
}
|
|
70
80
|
return this._statesClient.getStateAsync(id);
|
|
71
81
|
};
|
|
72
|
-
this.setState = (
|
|
82
|
+
this.setState = ((...args) => {
|
|
73
83
|
if (!this._statesClient) {
|
|
74
|
-
throw new Error(
|
|
84
|
+
throw new Error('States DB is not running');
|
|
75
85
|
}
|
|
76
86
|
return this._statesClient.setStateAsync(...args);
|
|
77
87
|
});
|
|
78
|
-
this.delState =
|
|
88
|
+
this.delState = (...args) => {
|
|
79
89
|
if (!this._statesClient) {
|
|
80
|
-
throw new Error(
|
|
90
|
+
throw new Error('States DB is not running');
|
|
81
91
|
}
|
|
82
92
|
return this._statesClient.delStateAsync(...args);
|
|
83
93
|
};
|
|
84
|
-
this.getObjectViewAsync =
|
|
94
|
+
this.getObjectViewAsync = (...args) => {
|
|
85
95
|
if (!this._objectsClient) {
|
|
86
|
-
throw new Error(
|
|
96
|
+
throw new Error('Objects DB is not running');
|
|
87
97
|
}
|
|
88
98
|
return this._objectsClient.getObjectViewAsync(...args);
|
|
89
99
|
};
|
|
@@ -102,36 +112,38 @@ class DBConnection extends events_1.default {
|
|
|
102
112
|
return this.getSystemConfig().objects.type;
|
|
103
113
|
}
|
|
104
114
|
get objectsPath() {
|
|
105
|
-
return path.join(this.testDataDir, this.objectsType ===
|
|
115
|
+
return path.join(this.testDataDir, this.objectsType === 'file' ? 'objects.json' : 'objects.jsonl');
|
|
106
116
|
}
|
|
107
117
|
get statesType() {
|
|
108
118
|
return this.getSystemConfig().states.type;
|
|
109
119
|
}
|
|
110
120
|
get statesPath() {
|
|
111
|
-
return path.join(this.testDataDir, this.statesType ===
|
|
121
|
+
return path.join(this.testDataDir, this.statesType === 'file' ? 'states.json' : 'states.jsonl');
|
|
112
122
|
}
|
|
113
123
|
getSystemConfig() {
|
|
114
124
|
const systemFilename = path.join(this.testDataDir, `${this.appName}.json`);
|
|
115
125
|
return (0, fs_extra_1.readJSONSync)(systemFilename);
|
|
116
126
|
}
|
|
117
127
|
async backup() {
|
|
118
|
-
debug(
|
|
128
|
+
debug('Creating DB backup...');
|
|
119
129
|
const wasRunning = this._isRunning;
|
|
120
130
|
await this.stop();
|
|
121
131
|
const objects = await (0, fs_extra_1.readFile)(this.objectsPath);
|
|
122
132
|
const states = await (0, fs_extra_1.readFile)(this.statesPath);
|
|
123
|
-
if (wasRunning)
|
|
133
|
+
if (wasRunning) {
|
|
124
134
|
await this.start();
|
|
135
|
+
}
|
|
125
136
|
return { objects, states };
|
|
126
137
|
}
|
|
127
138
|
async restore(objects, states) {
|
|
128
|
-
debug(
|
|
139
|
+
debug('Restoring DB backup...');
|
|
129
140
|
const wasRunning = this._isRunning;
|
|
130
141
|
await this.stop();
|
|
131
142
|
await (0, fs_extra_1.writeFile)(this.objectsPath, objects);
|
|
132
143
|
await (0, fs_extra_1.writeFile)(this.statesPath, states);
|
|
133
|
-
if (wasRunning)
|
|
144
|
+
if (wasRunning) {
|
|
134
145
|
await this.start();
|
|
146
|
+
}
|
|
135
147
|
}
|
|
136
148
|
setSystemConfig(systemConfig) {
|
|
137
149
|
const systemFilename = path.join(this.testDataDir, `${this.appName}.json`);
|
|
@@ -142,21 +154,21 @@ class DBConnection extends events_1.default {
|
|
|
142
154
|
}
|
|
143
155
|
async start() {
|
|
144
156
|
if (this._isRunning) {
|
|
145
|
-
debug(
|
|
157
|
+
debug('At least one DB instance is already running, not starting again...');
|
|
146
158
|
return;
|
|
147
159
|
}
|
|
148
|
-
debug(
|
|
160
|
+
debug('starting DB instances...');
|
|
149
161
|
await this.createObjectsDB();
|
|
150
162
|
await this.createStatesDB();
|
|
151
163
|
this._isRunning = true;
|
|
152
|
-
debug(
|
|
164
|
+
debug('DB instances started');
|
|
153
165
|
}
|
|
154
166
|
async stop() {
|
|
155
167
|
if (!this._isRunning) {
|
|
156
|
-
debug(
|
|
168
|
+
debug('No DB instance is running, nothing to stop...');
|
|
157
169
|
return;
|
|
158
170
|
}
|
|
159
|
-
debug(
|
|
171
|
+
debug('Stopping DB instances...');
|
|
160
172
|
// Stop clients before servers
|
|
161
173
|
await this._objectsClient?.destroy();
|
|
162
174
|
await this._objectsServer?.destroy();
|
|
@@ -167,36 +179,32 @@ class DBConnection extends events_1.default {
|
|
|
167
179
|
this._statesClient = null;
|
|
168
180
|
this._statesServer = null;
|
|
169
181
|
this._isRunning = false;
|
|
170
|
-
debug(
|
|
182
|
+
debug('DB instances stopped');
|
|
171
183
|
}
|
|
172
184
|
/** Creates the objects DB and sets up listeners for it */
|
|
173
185
|
async createObjectsDB() {
|
|
174
|
-
debug(
|
|
186
|
+
debug('creating objects DB');
|
|
175
187
|
const objectsType = this.objectsType;
|
|
176
188
|
debug(` => objects DB type: ${objectsType}`);
|
|
177
189
|
const settings = {
|
|
178
190
|
connection: {
|
|
179
191
|
type: objectsType,
|
|
180
|
-
host:
|
|
192
|
+
host: '127.0.0.1',
|
|
181
193
|
port: 19001,
|
|
182
|
-
user:
|
|
183
|
-
pass:
|
|
194
|
+
user: '',
|
|
195
|
+
pass: '',
|
|
184
196
|
noFileCache: false,
|
|
185
197
|
connectTimeout: 2000,
|
|
186
198
|
},
|
|
187
199
|
logger: this.logger,
|
|
188
200
|
};
|
|
189
201
|
const objectsDbPath = require.resolve(`@iobroker/db-objects-${objectsType}`, {
|
|
190
|
-
paths: [
|
|
191
|
-
path.join(this.testDir, "node_modules"),
|
|
192
|
-
path.join(this.testControllerDir, "node_modules"),
|
|
193
|
-
],
|
|
202
|
+
paths: [path.join(this.testDir, 'node_modules'), path.join(this.testControllerDir, 'node_modules')],
|
|
194
203
|
});
|
|
195
204
|
debug(` => objects DB lib found at ${objectsDbPath}`);
|
|
196
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
197
205
|
const { Server, Client } = require(objectsDbPath);
|
|
198
206
|
// First create the server
|
|
199
|
-
await new Promise(
|
|
207
|
+
await new Promise(resolve => {
|
|
200
208
|
this._objectsServer = new Server({
|
|
201
209
|
...settings,
|
|
202
210
|
connected: () => {
|
|
@@ -205,17 +213,17 @@ class DBConnection extends events_1.default {
|
|
|
205
213
|
});
|
|
206
214
|
});
|
|
207
215
|
// Then the client
|
|
208
|
-
await new Promise(
|
|
216
|
+
await new Promise(resolve => {
|
|
209
217
|
this._objectsClient = new Client({
|
|
210
218
|
...settings,
|
|
211
219
|
connected: () => {
|
|
212
|
-
this._objectsClient.subscribe(
|
|
220
|
+
this._objectsClient.subscribe('*');
|
|
213
221
|
resolve();
|
|
214
222
|
},
|
|
215
|
-
change: this.emit.bind(this,
|
|
223
|
+
change: this.emit.bind(this, 'objectChange'),
|
|
216
224
|
});
|
|
217
225
|
});
|
|
218
|
-
debug(
|
|
226
|
+
debug(' => done!');
|
|
219
227
|
}
|
|
220
228
|
/** Creates the states DB and sets up listeners for it */
|
|
221
229
|
async createStatesDB() {
|
|
@@ -225,7 +233,7 @@ class DBConnection extends events_1.default {
|
|
|
225
233
|
const settings = {
|
|
226
234
|
connection: {
|
|
227
235
|
type: statesType,
|
|
228
|
-
host:
|
|
236
|
+
host: '127.0.0.1',
|
|
229
237
|
port: 19000,
|
|
230
238
|
options: {
|
|
231
239
|
auth_pass: null,
|
|
@@ -235,16 +243,12 @@ class DBConnection extends events_1.default {
|
|
|
235
243
|
logger: this.logger,
|
|
236
244
|
};
|
|
237
245
|
const statesDbPath = require.resolve(`@iobroker/db-states-${statesType}`, {
|
|
238
|
-
paths: [
|
|
239
|
-
path.join(this.testDir, "node_modules"),
|
|
240
|
-
path.join(this.testControllerDir, "node_modules"),
|
|
241
|
-
],
|
|
246
|
+
paths: [path.join(this.testDir, 'node_modules'), path.join(this.testControllerDir, 'node_modules')],
|
|
242
247
|
});
|
|
243
248
|
debug(` => states DB lib found at ${statesDbPath}`);
|
|
244
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
245
249
|
const { Server, Client } = require(statesDbPath);
|
|
246
250
|
// First create the server
|
|
247
|
-
await new Promise(
|
|
251
|
+
await new Promise(resolve => {
|
|
248
252
|
this._statesServer = new Server({
|
|
249
253
|
...settings,
|
|
250
254
|
connected: () => {
|
|
@@ -253,43 +257,41 @@ class DBConnection extends events_1.default {
|
|
|
253
257
|
});
|
|
254
258
|
});
|
|
255
259
|
// Then the client
|
|
256
|
-
await new Promise(
|
|
260
|
+
await new Promise(resolve => {
|
|
257
261
|
this._statesClient = new Client({
|
|
258
262
|
...settings,
|
|
259
263
|
connected: () => {
|
|
260
|
-
this._statesClient.subscribe(
|
|
264
|
+
this._statesClient.subscribe('*');
|
|
261
265
|
resolve();
|
|
262
266
|
},
|
|
263
|
-
change: this.emit.bind(this,
|
|
267
|
+
change: this.emit.bind(this, 'stateChange'),
|
|
264
268
|
});
|
|
265
269
|
});
|
|
266
|
-
debug(
|
|
270
|
+
debug(' => done!');
|
|
267
271
|
}
|
|
268
272
|
subscribeMessage(id) {
|
|
269
273
|
if (!this._statesClient) {
|
|
270
|
-
throw new Error(
|
|
274
|
+
throw new Error('States DB is not running');
|
|
271
275
|
}
|
|
272
276
|
return this._statesClient.subscribeMessage(id);
|
|
273
277
|
}
|
|
274
278
|
pushMessage(instanceId, msg, callback) {
|
|
275
279
|
if (!this._statesClient) {
|
|
276
|
-
throw new Error(
|
|
280
|
+
throw new Error('States DB is not running');
|
|
277
281
|
}
|
|
278
282
|
this._statesClient.pushMessage(instanceId, msg, callback);
|
|
279
283
|
}
|
|
280
|
-
|
|
284
|
+
getStateIDs(pattern = '*') {
|
|
281
285
|
if (!this._statesClient) {
|
|
282
|
-
throw new Error(
|
|
286
|
+
throw new Error('States DB is not running');
|
|
283
287
|
}
|
|
284
|
-
return
|
|
285
|
-
this._statesClient.getKeys?.(pattern));
|
|
288
|
+
return this._statesClient.getKeysAsync?.(pattern) || this._statesClient.getKeys?.(pattern);
|
|
286
289
|
}
|
|
287
|
-
|
|
290
|
+
getObjectIDs(pattern = '*') {
|
|
288
291
|
if (!this._objectsClient) {
|
|
289
|
-
throw new Error(
|
|
292
|
+
throw new Error('Objects DB is not running');
|
|
290
293
|
}
|
|
291
|
-
return
|
|
292
|
-
this._objectsClient.getKeys?.(pattern));
|
|
294
|
+
return this._objectsClient.getKeysAsync?.(pattern) || this._objectsClient.getKeys?.(pattern);
|
|
293
295
|
}
|
|
294
296
|
}
|
|
295
297
|
exports.DBConnection = DBConnection;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { ChildProcess } from
|
|
2
|
-
import { EventEmitter } from
|
|
3
|
-
import type { DBConnection } from
|
|
1
|
+
import { type ChildProcess } from 'child_process';
|
|
2
|
+
import { EventEmitter } from 'events';
|
|
3
|
+
import type { DBConnection } from './dbConnection';
|
|
4
4
|
export interface TestHarness {
|
|
5
|
-
on(event:
|
|
6
|
-
on(event:
|
|
7
|
-
on(event:
|
|
5
|
+
on(event: 'objectChange', handler: ioBroker.ObjectChangeHandler): this;
|
|
6
|
+
on(event: 'stateChange', handler: ioBroker.StateChangeHandler): this;
|
|
7
|
+
on(event: 'failed', handler: (codeOrSignal: number | string) => void): this;
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* The test harness capsules the execution of the JS-Controller and the adapter instance and monitors their status.
|
|
@@ -41,11 +41,13 @@ export declare class TestHarness extends EventEmitter {
|
|
|
41
41
|
stopController(): Promise<void>;
|
|
42
42
|
/**
|
|
43
43
|
* Starts the adapter in a separate process and monitors its status
|
|
44
|
+
*
|
|
44
45
|
* @param env Additional environment variables to set
|
|
45
46
|
*/
|
|
46
47
|
startAdapter(env?: NodeJS.ProcessEnv): Promise<void>;
|
|
47
48
|
/**
|
|
48
49
|
* Starts the adapter in a separate process and resolves after it has started
|
|
50
|
+
*
|
|
49
51
|
* @param waitForConnection By default, the test will wait for the adapter's `alive` state to become true. Set this to `true` to wait for the `info.connection` state instead.
|
|
50
52
|
* @param env Additional environment variables to set
|
|
51
53
|
*/
|
|
@@ -60,7 +62,7 @@ export declare class TestHarness extends EventEmitter {
|
|
|
60
62
|
* Updates the adapter config. The changes can be a subset of the target object
|
|
61
63
|
*/
|
|
62
64
|
changeAdapterConfig(adapterName: string, changes: Record<string, any>): Promise<void>;
|
|
63
|
-
getAdapterExecutionMode(): ioBroker.AdapterCommon[
|
|
65
|
+
getAdapterExecutionMode(): ioBroker.AdapterCommon['mode'];
|
|
64
66
|
/** Enables the sendTo method */
|
|
65
67
|
enableSendTo(): Promise<void>;
|
|
66
68
|
private sendToID;
|
|
@@ -15,13 +15,23 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
|
|
|
15
15
|
}) : function(o, v) {
|
|
16
16
|
o["default"] = v;
|
|
17
17
|
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || function (
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
25
35
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
37
|
};
|
|
@@ -35,9 +45,9 @@ const events_1 = require("events");
|
|
|
35
45
|
const path = __importStar(require("path"));
|
|
36
46
|
const adapterTools_1 = require("../../../lib/adapterTools");
|
|
37
47
|
const tools_1 = require("./tools");
|
|
38
|
-
const debug = (0, debug_1.default)(
|
|
48
|
+
const debug = (0, debug_1.default)('testing:integration:TestHarness');
|
|
39
49
|
const isWindows = /^win/.test(process.platform);
|
|
40
|
-
const fromAdapterID =
|
|
50
|
+
const fromAdapterID = 'system.adapter.test.0';
|
|
41
51
|
/**
|
|
42
52
|
* The test harness capsules the execution of the JS-Controller and the adapter instance and monitors their status.
|
|
43
53
|
* Use it in every test to start a fresh adapter instance
|
|
@@ -53,7 +63,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
53
63
|
this.testDir = testDir;
|
|
54
64
|
this.dbConnection = dbConnection;
|
|
55
65
|
this.sendToID = 1;
|
|
56
|
-
debug(
|
|
66
|
+
debug('Creating instance');
|
|
57
67
|
this.adapterName = (0, adapterTools_1.getAdapterName)(this.adapterDir);
|
|
58
68
|
this.appName = (0, adapterTools_1.getAppName)(adapterDir);
|
|
59
69
|
this.testControllerDir = (0, tools_1.getTestControllerDir)(this.appName, testDir);
|
|
@@ -63,24 +73,24 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
63
73
|
debug(` adapter: ${this.testAdapterDir}`);
|
|
64
74
|
debug(` appName: ${this.appName}`);
|
|
65
75
|
debug(` adapterName: ${this.adapterName}`);
|
|
66
|
-
dbConnection.on(
|
|
67
|
-
this.emit(
|
|
76
|
+
dbConnection.on('objectChange', (id, obj) => {
|
|
77
|
+
this.emit('objectChange', id, obj);
|
|
68
78
|
});
|
|
69
|
-
dbConnection.on(
|
|
70
|
-
this.emit(
|
|
79
|
+
dbConnection.on('stateChange', (id, state) => {
|
|
80
|
+
this.emit('stateChange', id, state);
|
|
71
81
|
});
|
|
72
82
|
}
|
|
73
83
|
/** Gives direct access to the Objects DB */
|
|
74
84
|
get objects() {
|
|
75
85
|
if (!this.dbConnection.objectsClient) {
|
|
76
|
-
throw new Error(
|
|
86
|
+
throw new Error('Objects DB is not running');
|
|
77
87
|
}
|
|
78
88
|
return this.dbConnection.objectsClient;
|
|
79
89
|
}
|
|
80
90
|
/** Gives direct access to the States DB */
|
|
81
91
|
get states() {
|
|
82
92
|
if (!this.dbConnection.statesClient) {
|
|
83
|
-
throw new Error(
|
|
93
|
+
throw new Error('States DB is not running');
|
|
84
94
|
}
|
|
85
95
|
return this.dbConnection.statesClient;
|
|
86
96
|
}
|
|
@@ -104,59 +114,67 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
104
114
|
}
|
|
105
115
|
/** Stops the controller instance (and the adapter if it is running) */
|
|
106
116
|
async stopController() {
|
|
107
|
-
if (!this.isControllerRunning())
|
|
117
|
+
if (!this.isControllerRunning()) {
|
|
108
118
|
return;
|
|
119
|
+
}
|
|
109
120
|
if (!this.didAdapterStop()) {
|
|
110
|
-
debug(
|
|
121
|
+
debug('Stopping adapter instance...');
|
|
111
122
|
// Give the adapter time to stop (as long as configured in the io-package.json)
|
|
112
123
|
let stopTimeout;
|
|
113
124
|
try {
|
|
114
|
-
stopTimeout = (await this.dbConnection.getObject(`system.adapter.${this.adapterName}.0`))
|
|
125
|
+
stopTimeout = (await this.dbConnection.getObject(`system.adapter.${this.adapterName}.0`))
|
|
126
|
+
.common.stopTimeout;
|
|
115
127
|
stopTimeout += 1000;
|
|
116
128
|
}
|
|
117
|
-
catch {
|
|
118
|
-
|
|
129
|
+
catch {
|
|
130
|
+
// ignore
|
|
131
|
+
}
|
|
132
|
+
stopTimeout ||= 5000; // default 5s
|
|
119
133
|
debug(` => giving it ${stopTimeout}ms to terminate`);
|
|
120
134
|
await Promise.race([this.stopAdapter(), (0, async_1.wait)(stopTimeout)]);
|
|
121
135
|
if (this.isAdapterRunning()) {
|
|
122
|
-
debug(
|
|
123
|
-
this._adapterProcess.kill(
|
|
136
|
+
debug('Adapter did not terminate, killing it');
|
|
137
|
+
this._adapterProcess.kill('SIGKILL');
|
|
124
138
|
}
|
|
125
139
|
else {
|
|
126
|
-
debug(
|
|
140
|
+
debug('Adapter terminated');
|
|
127
141
|
}
|
|
128
142
|
}
|
|
129
143
|
else {
|
|
130
|
-
debug(
|
|
144
|
+
debug('Adapter failed to start - no need to terminate!');
|
|
131
145
|
}
|
|
132
146
|
await this.dbConnection.stop();
|
|
133
147
|
}
|
|
134
148
|
/**
|
|
135
149
|
* Starts the adapter in a separate process and monitors its status
|
|
150
|
+
*
|
|
136
151
|
* @param env Additional environment variables to set
|
|
137
152
|
*/
|
|
138
153
|
async startAdapter(env = {}) {
|
|
139
|
-
if (this.isAdapterRunning())
|
|
140
|
-
throw new Error(
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
if (this.isAdapterRunning()) {
|
|
155
|
+
throw new Error('The adapter is already running!');
|
|
156
|
+
}
|
|
157
|
+
else if (this.didAdapterStop()) {
|
|
158
|
+
throw new Error('This test harness has already been used. Please create a new one for each test!');
|
|
159
|
+
}
|
|
143
160
|
const mainFileAbsolute = await (0, adapterTools_1.locateAdapterMainFile)(this.testAdapterDir);
|
|
144
161
|
const mainFileRelative = path.relative(this.testAdapterDir, mainFileAbsolute);
|
|
145
162
|
const onClose = (code, signal) => {
|
|
146
163
|
this._adapterProcess.removeAllListeners();
|
|
147
164
|
this._adapterExit = code != undefined ? code : signal;
|
|
148
|
-
this.emit(
|
|
165
|
+
this.emit('failed', this._adapterExit);
|
|
149
166
|
};
|
|
150
|
-
this._adapterProcess = (0, child_process_1.spawn)(isWindows ?
|
|
167
|
+
this._adapterProcess = (0, child_process_1.spawn)(isWindows ? 'node.exe' : 'node', [mainFileRelative, '--console'], {
|
|
151
168
|
cwd: this.testAdapterDir,
|
|
152
|
-
stdio: [
|
|
169
|
+
stdio: ['inherit', 'inherit', 'inherit'],
|
|
153
170
|
env: { ...process.env, ...env },
|
|
154
171
|
})
|
|
155
|
-
.on(
|
|
156
|
-
.on(
|
|
172
|
+
.on('close', onClose)
|
|
173
|
+
.on('exit', onClose);
|
|
157
174
|
}
|
|
158
175
|
/**
|
|
159
176
|
* Starts the adapter in a separate process and resolves after it has started
|
|
177
|
+
*
|
|
160
178
|
* @param waitForConnection By default, the test will wait for the adapter's `alive` state to become true. Set this to `true` to wait for the `info.connection` state instead.
|
|
161
179
|
* @param env Additional environment variables to set
|
|
162
180
|
*/
|
|
@@ -165,13 +183,13 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
165
183
|
const waitForStateId = waitForConnection
|
|
166
184
|
? `${this.adapterName}.0.info.connection`
|
|
167
185
|
: `system.adapter.${this.adapterName}.0.alive`;
|
|
168
|
-
this.on(
|
|
186
|
+
void this.on('stateChange', (id, state) => {
|
|
169
187
|
if (id === waitForStateId && state && state.val === true) {
|
|
170
188
|
resolve();
|
|
171
189
|
}
|
|
172
190
|
})
|
|
173
|
-
.on(
|
|
174
|
-
reject(new Error(`The adapter startup was interrupted unexpectedly with ${typeof code ===
|
|
191
|
+
.on('failed', code => {
|
|
192
|
+
reject(new Error(`The adapter startup was interrupted unexpectedly with ${typeof code === 'number' ? 'code' : 'signal'} ${code}`));
|
|
175
193
|
})
|
|
176
194
|
.startAdapter(env);
|
|
177
195
|
});
|
|
@@ -186,33 +204,34 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
186
204
|
}
|
|
187
205
|
/** Stops the adapter process */
|
|
188
206
|
stopAdapter() {
|
|
189
|
-
if (!this.isAdapterRunning())
|
|
207
|
+
if (!this.isAdapterRunning()) {
|
|
190
208
|
return;
|
|
209
|
+
}
|
|
210
|
+
// eslint-disable-next-line no-async-promise-executor
|
|
191
211
|
return new Promise(async (resolve) => {
|
|
192
212
|
const onClose = (code, signal) => {
|
|
193
|
-
if (!this._adapterProcess)
|
|
213
|
+
if (!this._adapterProcess) {
|
|
194
214
|
return;
|
|
215
|
+
}
|
|
195
216
|
this._adapterProcess.removeAllListeners();
|
|
196
217
|
this._adapterExit = code != undefined ? code : signal;
|
|
197
218
|
this._adapterProcess = undefined;
|
|
198
|
-
debug(
|
|
219
|
+
debug('Adapter process terminated:');
|
|
199
220
|
debug(` Code: ${code}`);
|
|
200
221
|
debug(` Signal: ${signal}`);
|
|
201
222
|
resolve();
|
|
202
223
|
};
|
|
203
|
-
this._adapterProcess.removeAllListeners()
|
|
204
|
-
.on("close", onClose)
|
|
205
|
-
.on("exit", onClose);
|
|
224
|
+
this._adapterProcess.removeAllListeners().on('close', onClose).on('exit', onClose);
|
|
206
225
|
// Tell adapter to stop
|
|
207
226
|
try {
|
|
208
227
|
await this.dbConnection.setState(`system.adapter.${this.adapterName}.0.sigKill`, {
|
|
209
228
|
val: -1,
|
|
210
|
-
from:
|
|
229
|
+
from: 'system.host.testing',
|
|
211
230
|
});
|
|
212
231
|
}
|
|
213
232
|
catch {
|
|
214
233
|
// DB connection may be closed already, kill the process
|
|
215
|
-
this._adapterProcess?.kill(
|
|
234
|
+
this._adapterProcess?.kill('SIGTERM');
|
|
216
235
|
}
|
|
217
236
|
});
|
|
218
237
|
}
|
|
@@ -233,7 +252,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
233
252
|
/** Enables the sendTo method */
|
|
234
253
|
async enableSendTo() {
|
|
235
254
|
await this.dbConnection.setObject(fromAdapterID, {
|
|
236
|
-
type:
|
|
255
|
+
type: 'instance',
|
|
237
256
|
common: {},
|
|
238
257
|
native: {},
|
|
239
258
|
instanceObjects: [],
|
|
@@ -246,10 +265,10 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
246
265
|
const stateChangedHandler = (id, state) => {
|
|
247
266
|
if (id === `messagebox.${fromAdapterID}`) {
|
|
248
267
|
callback(state.message);
|
|
249
|
-
this.removeListener(
|
|
268
|
+
this.removeListener('stateChange', stateChangedHandler);
|
|
250
269
|
}
|
|
251
270
|
};
|
|
252
|
-
this.addListener(
|
|
271
|
+
this.addListener('stateChange', stateChangedHandler);
|
|
253
272
|
this.dbConnection.pushMessage(`system.adapter.${target}`, {
|
|
254
273
|
command: command,
|
|
255
274
|
message: message,
|
|
@@ -260,7 +279,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
260
279
|
ack: false,
|
|
261
280
|
time: Date.now(),
|
|
262
281
|
},
|
|
263
|
-
}, (err, id) => console.log(
|
|
282
|
+
}, (err, id) => console.log(`published message ${id}`));
|
|
264
283
|
}
|
|
265
284
|
}
|
|
266
285
|
exports.TestHarness = TestHarness;
|
|
@@ -10,7 +10,7 @@ var LoglevelOrder;
|
|
|
10
10
|
LoglevelOrder[LoglevelOrder["silly"] = 4] = "silly";
|
|
11
11
|
})(LoglevelOrder || (LoglevelOrder = {}));
|
|
12
12
|
function createLogger(loglevel) {
|
|
13
|
-
const loglevelNumeric = LoglevelOrder[loglevel ??
|
|
13
|
+
const loglevelNumeric = LoglevelOrder[loglevel ?? 'debug'] ?? LoglevelOrder.debug;
|
|
14
14
|
const ignore = () => { };
|
|
15
15
|
return {
|
|
16
16
|
error: loglevelNumeric >= LoglevelOrder.error ? console.error : ignore,
|