@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.
@@ -18,93 +18,287 @@ var __importStar = (this && this.__importStar) || function (mod) {
18
18
  __setModuleDefault(result, mod);
19
19
  return result;
20
20
  };
21
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
22
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
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
- /** The DB connection capsules access to the objects.json and states.json on disk */
36
- class DBConnection {
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.testDataDir = tools_1.getTestDataDir(appName, testDir);
45
- this.objectsPath = path.join(this.testDataDir, "objects.json");
46
- this.statesPath = path.join(this.testDataDir, "states.json");
47
- }
48
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
49
- readObjectsDB() {
50
- return __awaiter(this, void 0, void 0, function* () {
51
- // debug(`reading objects db...`);
52
- // debug(` dataDir: ${dataDir}`);
53
- // debug(` objectsPath: ${objectsPath}`);
54
- if (yield fs_extra_1.pathExists(this.objectsPath)) {
55
- // debug(` exists: true`);
56
- return fs_extra_1.readJSON(this.objectsPath, { encoding: "utf8" });
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
- writeObjectsDB(objects) {
61
- return __awaiter(this, void 0, void 0, function* () {
62
- if (!objects)
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
- writeStatesDB(states) {
68
- return __awaiter(this, void 0, void 0, function* () {
69
- if (!states)
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
- readStatesDB() {
75
- return __awaiter(this, void 0, void 0, function* () {
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
- * Creates a backup of the objects and states DB, so it can be restored after each test
87
- * @param appName The branded name of "iobroker"
88
- * @param testDir The directory the integration tests are executed in
89
- */
90
- // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
91
- readDB() {
92
- return __awaiter(this, void 0, void 0, function* () {
93
- const objects = yield this.readObjectsDB();
94
- const states = yield this.readStatesDB();
95
- return { objects, states };
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
- * Restores a previous backup of the objects and states DB
100
- * @param appName The branded name of "iobroker"
101
- * @param testDir The directory the integration tests are executed in
102
- */
103
- writeDB(objects, states) {
104
- return __awaiter(this, void 0, void 0, function* () {
105
- yield this.writeObjectsDB(objects);
106
- yield this.writeStatesDB(states);
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
- private dbConnection;
27
- private _objects;
28
- /** The actual objects DB */
28
+ /** Gives direct access to the Objects DB */
29
29
  get objects(): any;
30
- private _states;
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(appName: string, testDir: string, adapterName: string, changes: any): Promise<void>;
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;