@iobroker/testing 2.5.0 → 2.5.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -1
- package/LICENSE +1 -1
- package/build/lib/adapterTools.js +27 -38
- package/build/lib/executeCommand.js +3 -4
- package/build/tests/index.js +1 -0
- package/build/tests/integration/index.js +59 -76
- package/build/tests/integration/lib/adapterSetup.d.ts +4 -5
- package/build/tests/integration/lib/adapterSetup.js +81 -97
- package/build/tests/integration/lib/controllerSetup.d.ts +4 -7
- package/build/tests/integration/lib/controllerSetup.js +90 -105
- package/build/tests/integration/lib/dbConnection.d.ts +46 -22
- package/build/tests/integration/lib/dbConnection.js +259 -65
- package/build/tests/integration/lib/harness.d.ts +6 -11
- package/build/tests/integration/lib/harness.js +107 -192
- package/build/tests/integration/lib/tools.js +1 -1
- package/build/tests/packageFiles/index.js +32 -26
- package/build/tests/unit/index.js +2 -11
- package/build/tests/unit/mocks/mockAdapter.d.ts +1 -1
- package/build/tests/unit/mocks/mockAdapter.js +17 -18
- package/build/tests/unit/mocks/mockAdapterCore.d.ts +1 -1
- package/build/tests/unit/mocks/mockDatabase.d.ts +2 -2
- package/build/tests/unit/mocks/mockDatabase.js +10 -10
- package/build/tests/unit/mocks/mockLogger.js +8 -8
- package/build/tests/unit/mocks/tools.d.ts +1 -1
- package/build/tests/unit/mocks/tools.js +2 -2
- package/package.json +27 -25
|
@@ -18,93 +18,287 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
24
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
25
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
26
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
27
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
28
|
-
});
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
29
23
|
};
|
|
30
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
25
|
exports.DBConnection = void 0;
|
|
26
|
+
const debug_1 = __importDefault(require("debug"));
|
|
27
|
+
const events_1 = __importDefault(require("events"));
|
|
32
28
|
const fs_extra_1 = require("fs-extra");
|
|
33
29
|
const path = __importStar(require("path"));
|
|
34
30
|
const tools_1 = require("./tools");
|
|
35
|
-
|
|
36
|
-
|
|
31
|
+
const debug = (0, debug_1.default)("testing:integration:DBConnection");
|
|
32
|
+
/** The logger instance for the objects and states DB */
|
|
33
|
+
const logger = {
|
|
34
|
+
silly: console.log,
|
|
35
|
+
debug: console.log,
|
|
36
|
+
info: console.log,
|
|
37
|
+
warn: console.warn,
|
|
38
|
+
error: console.error,
|
|
39
|
+
};
|
|
40
|
+
/** The DB connection capsules access to the states and objects DB */
|
|
41
|
+
class DBConnection extends events_1.default {
|
|
37
42
|
/**
|
|
38
43
|
* @param appName The branded name of "iobroker"
|
|
39
44
|
* @param testDir The directory the integration tests are executed in
|
|
40
45
|
*/
|
|
41
46
|
constructor(appName, testDir) {
|
|
47
|
+
super();
|
|
42
48
|
this.appName = appName;
|
|
43
49
|
this.testDir = testDir;
|
|
44
|
-
this.
|
|
45
|
-
this.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
this._isRunning = false;
|
|
51
|
+
this.getObject = async (id) => {
|
|
52
|
+
if (!this._objectsClient) {
|
|
53
|
+
throw new Error("Objects DB is not running");
|
|
54
|
+
}
|
|
55
|
+
return this._objectsClient.getObjectAsync(id);
|
|
56
|
+
};
|
|
57
|
+
this.setObject = async (...args) => {
|
|
58
|
+
if (!this._objectsClient) {
|
|
59
|
+
throw new Error("Objects DB is not running");
|
|
60
|
+
}
|
|
61
|
+
return this._objectsClient.setObjectAsync(...args);
|
|
62
|
+
};
|
|
63
|
+
this.delObject = async (...args) => {
|
|
64
|
+
if (!this._objectsClient) {
|
|
65
|
+
throw new Error("Objects DB is not running");
|
|
66
|
+
}
|
|
67
|
+
return this._objectsClient.delObjectAsync(...args);
|
|
68
|
+
};
|
|
69
|
+
this.getState = async (id) => {
|
|
70
|
+
if (!this._statesClient) {
|
|
71
|
+
throw new Error("States DB is not running");
|
|
72
|
+
}
|
|
73
|
+
return this._statesClient.getStateAsync(id);
|
|
74
|
+
};
|
|
75
|
+
this.setState = (async (...args) => {
|
|
76
|
+
if (!this._statesClient) {
|
|
77
|
+
throw new Error("States DB is not running");
|
|
57
78
|
}
|
|
79
|
+
return this._statesClient.setStateAsync(...args);
|
|
58
80
|
});
|
|
81
|
+
this.delState = async (...args) => {
|
|
82
|
+
if (!this._statesClient) {
|
|
83
|
+
throw new Error("States DB is not running");
|
|
84
|
+
}
|
|
85
|
+
return this._statesClient.delStateAsync(...args);
|
|
86
|
+
};
|
|
87
|
+
this.getObjectViewAsync = async (...args) => {
|
|
88
|
+
if (!this._objectsClient) {
|
|
89
|
+
throw new Error("Objects DB is not running");
|
|
90
|
+
}
|
|
91
|
+
return this._objectsClient.getObjectViewAsync(...args);
|
|
92
|
+
};
|
|
93
|
+
this.testControllerDir = (0, tools_1.getTestControllerDir)(this.appName, testDir);
|
|
94
|
+
this.testDataDir = (0, tools_1.getTestDataDir)(appName, testDir);
|
|
59
95
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
return;
|
|
64
|
-
return fs_extra_1.writeJSON(this.objectsPath, objects);
|
|
65
|
-
});
|
|
96
|
+
/** The underlying objects client instance that can be used to access the objects DB */
|
|
97
|
+
get objectsClient() {
|
|
98
|
+
return this._objectsClient;
|
|
66
99
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return;
|
|
71
|
-
return fs_extra_1.writeJSON(this.statesPath, states);
|
|
72
|
-
});
|
|
100
|
+
/** The underlying states client instance that can be used to access the states DB */
|
|
101
|
+
get statesClient() {
|
|
102
|
+
return this._statesClient;
|
|
73
103
|
}
|
|
74
|
-
|
|
75
|
-
return
|
|
76
|
-
// debug(`reading states db...`);
|
|
77
|
-
// debug(` dataDir: ${dataDir}`);
|
|
78
|
-
// debug(` statesPath: ${statesPath}`);
|
|
79
|
-
if (yield fs_extra_1.pathExists(this.statesPath)) {
|
|
80
|
-
// debug(` exists: true`);
|
|
81
|
-
return fs_extra_1.readJSON(this.statesPath, { encoding: "utf8" });
|
|
82
|
-
}
|
|
83
|
-
});
|
|
104
|
+
get objectsType() {
|
|
105
|
+
return this.getSystemConfig().objects.type;
|
|
84
106
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
107
|
+
get objectsPath() {
|
|
108
|
+
return path.join(this.testDataDir, this.objectsType === "file" ? "objects.json" : "objects.jsonl");
|
|
109
|
+
}
|
|
110
|
+
get statesType() {
|
|
111
|
+
return this.getSystemConfig().states.type;
|
|
112
|
+
}
|
|
113
|
+
get statesPath() {
|
|
114
|
+
return path.join(this.testDataDir, this.statesType === "file" ? "states.json" : "states.jsonl");
|
|
115
|
+
}
|
|
116
|
+
getSystemConfig() {
|
|
117
|
+
const systemFilename = path.join(this.testDataDir, `${this.appName}.json`);
|
|
118
|
+
return (0, fs_extra_1.readJSONSync)(systemFilename);
|
|
119
|
+
}
|
|
120
|
+
async backup() {
|
|
121
|
+
debug("Creating DB backup...");
|
|
122
|
+
const wasRunning = this._isRunning;
|
|
123
|
+
await this.stop();
|
|
124
|
+
const objects = await (0, fs_extra_1.readFile)(this.objectsPath);
|
|
125
|
+
const states = await (0, fs_extra_1.readFile)(this.statesPath);
|
|
126
|
+
if (wasRunning)
|
|
127
|
+
await this.start();
|
|
128
|
+
return { objects, states };
|
|
129
|
+
}
|
|
130
|
+
async restore(objects, states) {
|
|
131
|
+
debug("Restoring DB backup...");
|
|
132
|
+
const wasRunning = this._isRunning;
|
|
133
|
+
await this.stop();
|
|
134
|
+
await (0, fs_extra_1.writeFile)(this.objectsPath, objects);
|
|
135
|
+
await (0, fs_extra_1.writeFile)(this.statesPath, states);
|
|
136
|
+
if (wasRunning)
|
|
137
|
+
await this.start();
|
|
138
|
+
}
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
140
|
+
setSystemConfig(systemConfig) {
|
|
141
|
+
const systemFilename = path.join(this.testDataDir, `${this.appName}.json`);
|
|
142
|
+
(0, fs_extra_1.writeJSONSync)(systemFilename, systemConfig, { spaces: 2 });
|
|
143
|
+
}
|
|
144
|
+
get isRunning() {
|
|
145
|
+
return this._isRunning;
|
|
146
|
+
}
|
|
147
|
+
async start() {
|
|
148
|
+
if (this._isRunning) {
|
|
149
|
+
debug("At least one DB instance is already running, not starting again...");
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
debug("starting DB instances...");
|
|
153
|
+
await this.createObjectsDB();
|
|
154
|
+
await this.createStatesDB();
|
|
155
|
+
this._isRunning = true;
|
|
156
|
+
debug("DB instances started");
|
|
157
|
+
}
|
|
158
|
+
async stop() {
|
|
159
|
+
var _a, _b, _c, _d;
|
|
160
|
+
if (!this._isRunning) {
|
|
161
|
+
debug("No DB instance is running, nothing to stop...");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
debug("Stopping DB instances...");
|
|
165
|
+
// Stop clients before servers
|
|
166
|
+
await ((_a = this._objectsClient) === null || _a === void 0 ? void 0 : _a.destroy());
|
|
167
|
+
await ((_b = this._objectsServer) === null || _b === void 0 ? void 0 : _b.destroy());
|
|
168
|
+
await ((_c = this._statesClient) === null || _c === void 0 ? void 0 : _c.destroy());
|
|
169
|
+
await ((_d = this._statesServer) === null || _d === void 0 ? void 0 : _d.destroy());
|
|
170
|
+
this._objectsClient = null;
|
|
171
|
+
this._objectsServer = null;
|
|
172
|
+
this._statesClient = null;
|
|
173
|
+
this._statesServer = null;
|
|
174
|
+
this._isRunning = false;
|
|
175
|
+
debug("DB instances stopped");
|
|
176
|
+
}
|
|
177
|
+
/** Creates the objects DB and sets up listeners for it */
|
|
178
|
+
async createObjectsDB() {
|
|
179
|
+
debug("creating objects DB");
|
|
180
|
+
const objectsType = this.objectsType;
|
|
181
|
+
debug(` => objects DB type: ${objectsType}`);
|
|
182
|
+
const settings = {
|
|
183
|
+
connection: {
|
|
184
|
+
type: objectsType,
|
|
185
|
+
host: "127.0.0.1",
|
|
186
|
+
port: 19001,
|
|
187
|
+
user: "",
|
|
188
|
+
pass: "",
|
|
189
|
+
noFileCache: false,
|
|
190
|
+
connectTimeout: 2000,
|
|
191
|
+
},
|
|
192
|
+
logger,
|
|
193
|
+
};
|
|
194
|
+
const objectsDbPath = require.resolve(`@iobroker/db-objects-${objectsType}`, {
|
|
195
|
+
paths: [
|
|
196
|
+
path.join(this.testDir, "node_modules"),
|
|
197
|
+
path.join(this.testControllerDir, "node_modules"),
|
|
198
|
+
],
|
|
199
|
+
});
|
|
200
|
+
debug(` => objects DB lib found at ${objectsDbPath}`);
|
|
201
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
202
|
+
const { Server, Client } = require(objectsDbPath);
|
|
203
|
+
// First create the server
|
|
204
|
+
await new Promise((resolve) => {
|
|
205
|
+
this._objectsServer = new Server({
|
|
206
|
+
...settings,
|
|
207
|
+
connected: () => {
|
|
208
|
+
resolve();
|
|
209
|
+
},
|
|
210
|
+
});
|
|
96
211
|
});
|
|
212
|
+
// Then the client
|
|
213
|
+
await new Promise((resolve) => {
|
|
214
|
+
this._objectsClient = new Client({
|
|
215
|
+
...settings,
|
|
216
|
+
connected: () => {
|
|
217
|
+
this._objectsClient.subscribe("*");
|
|
218
|
+
resolve();
|
|
219
|
+
},
|
|
220
|
+
change: this.emit.bind(this, "objectChange"),
|
|
221
|
+
});
|
|
222
|
+
});
|
|
223
|
+
debug(" => done!");
|
|
97
224
|
}
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
225
|
+
/** Creates the states DB and sets up listeners for it */
|
|
226
|
+
async createStatesDB() {
|
|
227
|
+
debug(`creating states DB`);
|
|
228
|
+
const statesType = this.statesType;
|
|
229
|
+
debug(` => states DB type: ${statesType}`);
|
|
230
|
+
const settings = {
|
|
231
|
+
connection: {
|
|
232
|
+
type: statesType,
|
|
233
|
+
host: "127.0.0.1",
|
|
234
|
+
port: 19000,
|
|
235
|
+
options: {
|
|
236
|
+
auth_pass: null,
|
|
237
|
+
retry_max_delay: 15000,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
logger,
|
|
241
|
+
};
|
|
242
|
+
const statesDbPath = require.resolve(`@iobroker/db-states-${statesType}`, {
|
|
243
|
+
paths: [
|
|
244
|
+
path.join(this.testDir, "node_modules"),
|
|
245
|
+
path.join(this.testControllerDir, "node_modules"),
|
|
246
|
+
],
|
|
247
|
+
});
|
|
248
|
+
debug(` => states DB lib found at ${statesDbPath}`);
|
|
249
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
250
|
+
const { Server, Client } = require(statesDbPath);
|
|
251
|
+
// First create the server
|
|
252
|
+
await new Promise((resolve) => {
|
|
253
|
+
this._statesServer = new Server({
|
|
254
|
+
...settings,
|
|
255
|
+
connected: () => {
|
|
256
|
+
resolve();
|
|
257
|
+
},
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
// Then the client
|
|
261
|
+
await new Promise((resolve) => {
|
|
262
|
+
this._statesClient = new Client({
|
|
263
|
+
...settings,
|
|
264
|
+
connected: () => {
|
|
265
|
+
this._statesClient.subscribe("*");
|
|
266
|
+
resolve();
|
|
267
|
+
},
|
|
268
|
+
change: this.emit.bind(this, "stateChange"),
|
|
269
|
+
});
|
|
107
270
|
});
|
|
271
|
+
debug(" => done!");
|
|
272
|
+
}
|
|
273
|
+
subscribeMessage(id) {
|
|
274
|
+
if (!this._statesClient) {
|
|
275
|
+
throw new Error("States DB is not running");
|
|
276
|
+
}
|
|
277
|
+
return this._statesClient.subscribeMessage(id);
|
|
278
|
+
}
|
|
279
|
+
pushMessage(instanceId,
|
|
280
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
281
|
+
msg, callback) {
|
|
282
|
+
if (!this._statesClient) {
|
|
283
|
+
throw new Error("States DB is not running");
|
|
284
|
+
}
|
|
285
|
+
this._statesClient.pushMessage(instanceId, msg, callback);
|
|
286
|
+
}
|
|
287
|
+
async getStateIDs(pattern = "*") {
|
|
288
|
+
var _a, _b, _c, _d;
|
|
289
|
+
if (!this._statesClient) {
|
|
290
|
+
throw new Error("States DB is not running");
|
|
291
|
+
}
|
|
292
|
+
return (((_b = (_a = this._statesClient).getKeysAsync) === null || _b === void 0 ? void 0 : _b.call(_a, pattern)) ||
|
|
293
|
+
((_d = (_c = this._statesClient).getKeys) === null || _d === void 0 ? void 0 : _d.call(_c, pattern)));
|
|
294
|
+
}
|
|
295
|
+
async getObjectIDs(pattern = "*") {
|
|
296
|
+
var _a, _b, _c, _d;
|
|
297
|
+
if (!this._objectsClient) {
|
|
298
|
+
throw new Error("Objects DB is not running");
|
|
299
|
+
}
|
|
300
|
+
return (((_b = (_a = this._objectsClient).getKeysAsync) === null || _b === void 0 ? void 0 : _b.call(_a, pattern)) ||
|
|
301
|
+
((_d = (_c = this._objectsClient).getKeys) === null || _d === void 0 ? void 0 : _d.call(_c, pattern)));
|
|
108
302
|
}
|
|
109
303
|
}
|
|
110
304
|
exports.DBConnection = DBConnection;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
/// <reference types="node" />
|
|
3
3
|
import { ChildProcess } from "child_process";
|
|
4
4
|
import { EventEmitter } from "events";
|
|
5
|
+
import type { DBConnection } from "./dbConnection";
|
|
5
6
|
export interface TestHarness {
|
|
6
7
|
on(event: "objectChange", handler: ioBroker.ObjectChangeHandler): this;
|
|
7
8
|
on(event: "stateChange", handler: ioBroker.StateChangeHandler): this;
|
|
@@ -14,21 +15,19 @@ export interface TestHarness {
|
|
|
14
15
|
export declare class TestHarness extends EventEmitter {
|
|
15
16
|
private adapterDir;
|
|
16
17
|
private testDir;
|
|
18
|
+
private dbConnection;
|
|
17
19
|
/**
|
|
18
20
|
* @param adapterDir The root directory of the adapter
|
|
19
21
|
* @param testDir The directory the integration tests are executed in
|
|
20
22
|
*/
|
|
21
|
-
constructor(adapterDir: string, testDir: string);
|
|
23
|
+
constructor(adapterDir: string, testDir: string, dbConnection: DBConnection);
|
|
22
24
|
private adapterName;
|
|
23
25
|
private appName;
|
|
24
26
|
private testControllerDir;
|
|
25
27
|
private testAdapterDir;
|
|
26
|
-
|
|
27
|
-
private _objects;
|
|
28
|
-
/** The actual objects DB */
|
|
28
|
+
/** Gives direct access to the Objects DB */
|
|
29
29
|
get objects(): any;
|
|
30
|
-
|
|
31
|
-
/** The actual states DB */
|
|
30
|
+
/** Gives direct access to the States DB */
|
|
32
31
|
get states(): any;
|
|
33
32
|
private _adapterProcess;
|
|
34
33
|
/** The process the adapter is running in */
|
|
@@ -36,10 +35,6 @@ export declare class TestHarness extends EventEmitter {
|
|
|
36
35
|
private _adapterExit;
|
|
37
36
|
/** Contains the adapter exit code or signal if it was terminated unexpectedly */
|
|
38
37
|
get adapterExit(): number | string | undefined;
|
|
39
|
-
/** Creates the objects DB and sets up listeners for it */
|
|
40
|
-
private createObjectsDB;
|
|
41
|
-
/** Creates the states DB and sets up listeners for it */
|
|
42
|
-
private createStatesDB;
|
|
43
38
|
/** Checks if the controller instance is running */
|
|
44
39
|
isControllerRunning(): boolean;
|
|
45
40
|
/** Starts the controller instance by creating the databases */
|
|
@@ -65,7 +60,7 @@ export declare class TestHarness extends EventEmitter {
|
|
|
65
60
|
/**
|
|
66
61
|
* Updates the adapter config. The changes can be a subset of the target object
|
|
67
62
|
*/
|
|
68
|
-
changeAdapterConfig(
|
|
63
|
+
changeAdapterConfig(adapterName: string, changes: Record<string, any>): Promise<void>;
|
|
69
64
|
/** Enables the sendTo method */
|
|
70
65
|
enableSendTo(): Promise<void>;
|
|
71
66
|
private sendToID;
|