@iobroker/testing 2.5.3 → 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 +3 -2
- package/build/tests/index.js +1 -0
- package/build/tests/integration/index.js +21 -23
- package/build/tests/integration/lib/adapterSetup.d.ts +3 -4
- package/build/tests/integration/lib/adapterSetup.js +18 -17
- package/build/tests/integration/lib/controllerSetup.d.ts +3 -6
- package/build/tests/integration/lib/controllerSetup.js +24 -22
- package/build/tests/integration/lib/dbConnection.d.ts +46 -22
- package/build/tests/integration/lib/dbConnection.js +261 -46
- package/build/tests/integration/lib/harness.d.ts +6 -11
- package/build/tests/integration/lib/harness.js +43 -104
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
PLACEHOLDER for next version:
|
|
5
5
|
## __WORK IN PROGRESS__
|
|
6
6
|
-->
|
|
7
|
-
## 2.5.
|
|
8
|
-
*
|
|
7
|
+
## 2.5.4 (2022-02-02)
|
|
8
|
+
* Modifying ioBroker databases now uses the same methods that JS-Controller uses internally. This ensures that the testing is compatible with the `jsonl` database format.
|
|
9
|
+
* Testing adapters with adapter dependencies that try to access the databases during installation now works.
|
|
9
10
|
|
|
10
11
|
## 2.5.2 (2021-09-18)
|
|
11
12
|
* Fix: `adminUI.config` is now respected for the config UI check and JSON config is allowed too
|
package/build/tests/index.js
CHANGED
|
@@ -8,6 +8,7 @@ const createMocks_1 = require("./unit/harness/createMocks");
|
|
|
8
8
|
const mockDatabase_1 = require("./unit/mocks/mockDatabase");
|
|
9
9
|
var harness_1 = require("./integration/lib/harness");
|
|
10
10
|
Object.defineProperty(exports, "IntegrationTestHarness", { enumerable: true, get: function () { return harness_1.TestHarness; } });
|
|
11
|
+
var mockAdapter_1 = require("./unit/mocks/mockAdapter");
|
|
11
12
|
var mockDatabase_2 = require("./unit/mocks/mockDatabase");
|
|
12
13
|
Object.defineProperty(exports, "MockDatabase", { enumerable: true, get: function () { return mockDatabase_2.MockDatabase; } });
|
|
13
14
|
/** Predefined test sets */
|
|
@@ -24,7 +24,6 @@ const async_1 = require("alcalzone-shared/async");
|
|
|
24
24
|
const os = __importStar(require("os"));
|
|
25
25
|
const path = __importStar(require("path"));
|
|
26
26
|
const adapterTools_1 = require("../../lib/adapterTools");
|
|
27
|
-
const executeCommand_1 = require("../../lib/executeCommand");
|
|
28
27
|
const adapterSetup_1 = require("./lib/adapterSetup");
|
|
29
28
|
const controllerSetup_1 = require("./lib/controllerSetup");
|
|
30
29
|
const dbConnection_1 = require("./lib/dbConnection");
|
|
@@ -33,9 +32,10 @@ function testAdapter(adapterDir, options = {}) {
|
|
|
33
32
|
const appName = (0, adapterTools_1.getAppName)(adapterDir);
|
|
34
33
|
const adapterName = (0, adapterTools_1.getAdapterName)(adapterDir);
|
|
35
34
|
const testDir = path.join(os.tmpdir(), `test-${appName}.${adapterName}`);
|
|
35
|
+
/** This db connection is only used for the lifetime of a test and then re-created */
|
|
36
|
+
let dbConnection;
|
|
36
37
|
let harness;
|
|
37
|
-
const
|
|
38
|
-
const controllerSetup = new controllerSetup_1.ControllerSetup(adapterDir, testDir, dbConnection);
|
|
38
|
+
const controllerSetup = new controllerSetup_1.ControllerSetup(adapterDir, testDir);
|
|
39
39
|
console.log();
|
|
40
40
|
console.log(`Running tests in ${testDir}`);
|
|
41
41
|
console.log();
|
|
@@ -49,46 +49,44 @@ function testAdapter(adapterDir, options = {}) {
|
|
|
49
49
|
if (await controllerSetup.isJsControllerRunning()) {
|
|
50
50
|
throw new Error("JS-Controller is already running! Stop it for the first test run and try again!");
|
|
51
51
|
}
|
|
52
|
-
const adapterSetup = new adapterSetup_1.AdapterSetup(adapterDir, testDir
|
|
52
|
+
const adapterSetup = new adapterSetup_1.AdapterSetup(adapterDir, testDir);
|
|
53
|
+
// Installation happens in two steps:
|
|
54
|
+
// First we need to set up JS Controller, so the databases etc. can be created
|
|
53
55
|
// First we need to copy all files and execute an npm install
|
|
54
56
|
await controllerSetup.prepareTestDir();
|
|
55
|
-
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
await (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (wasJsControllerInstalled)
|
|
64
|
-
await controllerSetup.setupJsController();
|
|
65
|
-
await controllerSetup.setupSystemConfig();
|
|
66
|
-
await controllerSetup.disableAdminInstances();
|
|
67
|
-
await adapterSetup.deleteOldInstances();
|
|
57
|
+
// Only then we can install the adapter, because some (including VIS) try to access
|
|
58
|
+
// the databases if JS Controller is installed
|
|
59
|
+
await adapterSetup.installAdapterInTestDir();
|
|
60
|
+
const dbConnection = new dbConnection_1.DBConnection(appName, testDir);
|
|
61
|
+
await dbConnection.start();
|
|
62
|
+
controllerSetup.setupSystemConfig(dbConnection);
|
|
63
|
+
await controllerSetup.disableAdminInstances(dbConnection);
|
|
64
|
+
await adapterSetup.deleteOldInstances(dbConnection);
|
|
68
65
|
await adapterSetup.addAdapterInstance();
|
|
66
|
+
await dbConnection.stop();
|
|
69
67
|
// Create a copy of the databases that we can restore later
|
|
70
68
|
({ objects: objectsBackup, states: statesBackup } =
|
|
71
|
-
await dbConnection.
|
|
69
|
+
await dbConnection.backup());
|
|
72
70
|
});
|
|
73
71
|
beforeEach(async function () {
|
|
74
72
|
this.timeout(30000);
|
|
73
|
+
dbConnection = new dbConnection_1.DBConnection(appName, testDir);
|
|
75
74
|
// Clean up before every single test
|
|
76
75
|
await Promise.all([
|
|
77
76
|
controllerSetup.clearDBDir(),
|
|
78
77
|
controllerSetup.clearLogDir(),
|
|
79
|
-
dbConnection.
|
|
78
|
+
dbConnection.restore(objectsBackup, statesBackup),
|
|
80
79
|
]);
|
|
81
80
|
// Create a new test harness
|
|
82
|
-
|
|
81
|
+
await dbConnection.start();
|
|
82
|
+
harness = new harness_1.TestHarness(adapterDir, testDir, dbConnection);
|
|
83
83
|
// Enable the adapter and set its loglevel to debug
|
|
84
|
-
await harness.changeAdapterConfig(
|
|
84
|
+
await harness.changeAdapterConfig(adapterName, {
|
|
85
85
|
common: {
|
|
86
86
|
enabled: true,
|
|
87
87
|
loglevel: "debug",
|
|
88
88
|
},
|
|
89
89
|
});
|
|
90
|
-
// Start the controller instance
|
|
91
|
-
await harness.startController();
|
|
92
90
|
// And enable the sendTo emulation
|
|
93
91
|
await harness.enableSendTo();
|
|
94
92
|
});
|
|
@@ -2,8 +2,7 @@ import type { DBConnection } from "./dbConnection";
|
|
|
2
2
|
export declare class AdapterSetup {
|
|
3
3
|
private adapterDir;
|
|
4
4
|
private testDir;
|
|
5
|
-
|
|
6
|
-
constructor(adapterDir: string, testDir: string, dbConnection: DBConnection);
|
|
5
|
+
constructor(adapterDir: string, testDir: string);
|
|
7
6
|
private testAdapterDir;
|
|
8
7
|
private adapterName;
|
|
9
8
|
private adapterFullName;
|
|
@@ -14,10 +13,10 @@ export declare class AdapterSetup {
|
|
|
14
13
|
*/
|
|
15
14
|
isAdapterInstalled(): Promise<boolean>;
|
|
16
15
|
/** Copies all adapter files (except a few) to the test directory */
|
|
17
|
-
|
|
16
|
+
installAdapterInTestDir(): Promise<void>;
|
|
18
17
|
/**
|
|
19
18
|
* Adds an instance for an already installed adapter in the test directory
|
|
20
19
|
*/
|
|
21
20
|
addAdapterInstance(): Promise<void>;
|
|
22
|
-
deleteOldInstances(): Promise<void>;
|
|
21
|
+
deleteOldInstances(dbConnection: DBConnection): Promise<void>;
|
|
23
22
|
}
|
|
@@ -23,6 +23,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.AdapterSetup = void 0;
|
|
26
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
26
27
|
// Add debug logging for tests
|
|
27
28
|
const objects_1 = require("alcalzone-shared/objects");
|
|
28
29
|
const debug_1 = __importDefault(require("debug"));
|
|
@@ -33,10 +34,9 @@ const executeCommand_1 = require("../../../lib/executeCommand");
|
|
|
33
34
|
const tools_1 = require("./tools");
|
|
34
35
|
const debug = (0, debug_1.default)("testing:integration:AdapterSetup");
|
|
35
36
|
class AdapterSetup {
|
|
36
|
-
constructor(adapterDir, testDir
|
|
37
|
+
constructor(adapterDir, testDir) {
|
|
37
38
|
this.adapterDir = adapterDir;
|
|
38
39
|
this.testDir = testDir;
|
|
39
|
-
this.dbConnection = dbConnection;
|
|
40
40
|
debug("Creating AdapterSetup...");
|
|
41
41
|
this.adapterName = (0, adapterTools_1.getAdapterName)(this.adapterDir);
|
|
42
42
|
this.adapterFullName = (0, adapterTools_1.getAdapterFullName)(this.adapterDir);
|
|
@@ -57,7 +57,7 @@ class AdapterSetup {
|
|
|
57
57
|
return (0, fs_extra_1.pathExists)(this.testAdapterDir);
|
|
58
58
|
}
|
|
59
59
|
/** Copies all adapter files (except a few) to the test directory */
|
|
60
|
-
async
|
|
60
|
+
async installAdapterInTestDir() {
|
|
61
61
|
debug("Copying adapter files to test directory...");
|
|
62
62
|
// We install the adapter almost like it would be installed in the real world
|
|
63
63
|
// Therefore pack it into a tarball and put it in the test dir for installation
|
|
@@ -87,6 +87,11 @@ class AdapterSetup {
|
|
|
87
87
|
debug("Deleting old remains of this adapter");
|
|
88
88
|
if (await (0, fs_extra_1.pathExists)(this.testAdapterDir))
|
|
89
89
|
await (0, fs_extra_1.remove)(this.testAdapterDir);
|
|
90
|
+
debug("Installing adapter");
|
|
91
|
+
// Defer to npm to install the controller (if it wasn't already)
|
|
92
|
+
await (0, executeCommand_1.executeCommand)("npm", ["i", "--production"], {
|
|
93
|
+
cwd: this.testDir,
|
|
94
|
+
});
|
|
90
95
|
debug(" => done!");
|
|
91
96
|
}
|
|
92
97
|
/**
|
|
@@ -109,9 +114,12 @@ class AdapterSetup {
|
|
|
109
114
|
throw new Error(`Adding the adapter instance failed!`);
|
|
110
115
|
debug(" => done!");
|
|
111
116
|
}
|
|
112
|
-
async deleteOldInstances() {
|
|
117
|
+
async deleteOldInstances(dbConnection) {
|
|
113
118
|
debug("Removing old adapter instances...");
|
|
114
|
-
const
|
|
119
|
+
const allKeys = new Set([
|
|
120
|
+
...(await dbConnection.getObjectIDs()),
|
|
121
|
+
...(await dbConnection.getStateIDs()),
|
|
122
|
+
]);
|
|
115
123
|
const instanceRegex = new RegExp(`^system\\.adapter\\.${this.adapterName}\\.\\d+`);
|
|
116
124
|
const instanceObjsRegex = new RegExp(`^${this.adapterName}\\.\\d+\.`);
|
|
117
125
|
const belongsToAdapter = (id) => {
|
|
@@ -120,19 +128,12 @@ class AdapterSetup {
|
|
|
120
128
|
id === this.adapterName ||
|
|
121
129
|
id === `${this.adapterName}.admin`);
|
|
122
130
|
};
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
if (states) {
|
|
130
|
-
for (const id of Object.keys(states)) {
|
|
131
|
-
if (belongsToAdapter(id))
|
|
132
|
-
delete states[id];
|
|
133
|
-
}
|
|
131
|
+
const idsToDelete = [...allKeys].filter((id) => belongsToAdapter(id));
|
|
132
|
+
for (const id of idsToDelete) {
|
|
133
|
+
await dbConnection.delObject(id).catch(() => { });
|
|
134
|
+
await dbConnection.delState(id).catch(() => { });
|
|
134
135
|
}
|
|
135
|
-
|
|
136
|
+
debug(" => done!");
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
exports.AdapterSetup = AdapterSetup;
|
|
@@ -2,8 +2,7 @@ import type { DBConnection } from "./dbConnection";
|
|
|
2
2
|
export declare class ControllerSetup {
|
|
3
3
|
private adapterDir;
|
|
4
4
|
private testDir;
|
|
5
|
-
|
|
6
|
-
constructor(adapterDir: string, testDir: string, dbConnection: DBConnection);
|
|
5
|
+
constructor(adapterDir: string, testDir: string);
|
|
7
6
|
private appName;
|
|
8
7
|
private adapterName;
|
|
9
8
|
private testAdapterDir;
|
|
@@ -22,8 +21,6 @@ export declare class ControllerSetup {
|
|
|
22
21
|
isJsControllerRunning(): Promise<boolean>;
|
|
23
22
|
/**
|
|
24
23
|
* Sets up an existing JS-Controller instance for testing by executing "iobroker setup first"
|
|
25
|
-
* @param appName The branded name of "iobroker"
|
|
26
|
-
* @param testDir The directory the integration tests are executed in
|
|
27
24
|
*/
|
|
28
25
|
setupJsController(): Promise<void>;
|
|
29
26
|
/**
|
|
@@ -31,7 +28,7 @@ export declare class ControllerSetup {
|
|
|
31
28
|
* @param appName The branded name of "iobroker"
|
|
32
29
|
* @param testDir The directory the integration tests are executed in
|
|
33
30
|
*/
|
|
34
|
-
setupSystemConfig():
|
|
31
|
+
setupSystemConfig(dbConnection: DBConnection): void;
|
|
35
32
|
/**
|
|
36
33
|
* Clears the log dir for integration tests (and creates it if it doesn't exist)
|
|
37
34
|
* @param appName The branded name of "iobroker"
|
|
@@ -48,5 +45,5 @@ export declare class ControllerSetup {
|
|
|
48
45
|
* Disables all admin instances in the objects DB
|
|
49
46
|
* @param objects The contents of objects.json
|
|
50
47
|
*/
|
|
51
|
-
disableAdminInstances(): Promise<void>;
|
|
48
|
+
disableAdminInstances(dbConnection: DBConnection): Promise<void>;
|
|
52
49
|
}
|
|
@@ -23,7 +23,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.ControllerSetup = void 0;
|
|
26
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
27
26
|
// Add debug logging for tests
|
|
28
27
|
const debug_1 = __importDefault(require("debug"));
|
|
29
28
|
const fs_extra_1 = require("fs-extra");
|
|
@@ -34,10 +33,9 @@ const executeCommand_1 = require("../../../lib/executeCommand");
|
|
|
34
33
|
const tools_1 = require("./tools");
|
|
35
34
|
const debug = (0, debug_1.default)("testing:integration:ControllerSetup");
|
|
36
35
|
class ControllerSetup {
|
|
37
|
-
constructor(adapterDir, testDir
|
|
36
|
+
constructor(adapterDir, testDir) {
|
|
38
37
|
this.adapterDir = adapterDir;
|
|
39
38
|
this.testDir = testDir;
|
|
40
|
-
this.dbConnection = dbConnection;
|
|
41
39
|
debug("Creating ControllerSetup...");
|
|
42
40
|
this.adapterName = (0, adapterTools_1.getAdapterName)(this.adapterDir);
|
|
43
41
|
this.appName = (0, adapterTools_1.getAppName)(this.adapterDir);
|
|
@@ -83,6 +81,16 @@ class ControllerSetup {
|
|
|
83
81
|
if (nodeMajorVersion >= 10) {
|
|
84
82
|
await (0, fs_extra_1.writeFile)(path.join(this.testDir, ".npmrc"), "engine-strict=true", "utf8");
|
|
85
83
|
}
|
|
84
|
+
// Remember if JS-Controller is installed already. If so, we need to call `setup first` afterwards
|
|
85
|
+
const wasJsControllerInstalled = await this.isJsControllerInstalled();
|
|
86
|
+
// Defer to npm to install the controller (if it wasn't already)
|
|
87
|
+
debug("(Re-)installing JS Controller...");
|
|
88
|
+
await (0, executeCommand_1.executeCommand)("npm", ["i", "--production"], {
|
|
89
|
+
cwd: this.testDir,
|
|
90
|
+
});
|
|
91
|
+
// Prepare/clean the databases and config
|
|
92
|
+
if (wasJsControllerInstalled)
|
|
93
|
+
await this.setupJsController();
|
|
86
94
|
debug(" => done!");
|
|
87
95
|
}
|
|
88
96
|
/**
|
|
@@ -152,8 +160,6 @@ class ControllerSetup {
|
|
|
152
160
|
// }
|
|
153
161
|
/**
|
|
154
162
|
* Sets up an existing JS-Controller instance for testing by executing "iobroker setup first"
|
|
155
|
-
* @param appName The branded name of "iobroker"
|
|
156
|
-
* @param testDir The directory the integration tests are executed in
|
|
157
163
|
*/
|
|
158
164
|
async setupJsController() {
|
|
159
165
|
debug("Initializing JS-Controller installation...");
|
|
@@ -175,15 +181,12 @@ class ControllerSetup {
|
|
|
175
181
|
* @param appName The branded name of "iobroker"
|
|
176
182
|
* @param testDir The directory the integration tests are executed in
|
|
177
183
|
*/
|
|
178
|
-
|
|
179
|
-
debug(`Moving databases to different ports
|
|
180
|
-
const
|
|
181
|
-
const systemConfig = require(systemFilename);
|
|
184
|
+
setupSystemConfig(dbConnection) {
|
|
185
|
+
debug(`Moving databases to different ports...`);
|
|
186
|
+
const systemConfig = dbConnection.getSystemConfig();
|
|
182
187
|
systemConfig.objects.port = 19001;
|
|
183
|
-
systemConfig.objects.type = "file";
|
|
184
188
|
systemConfig.states.port = 19000;
|
|
185
|
-
systemConfig
|
|
186
|
-
await (0, fs_extra_1.writeFile)(systemFilename, JSON.stringify(systemConfig, null, 2));
|
|
189
|
+
dbConnection.setSystemConfig(systemConfig);
|
|
187
190
|
debug(" => done!");
|
|
188
191
|
}
|
|
189
192
|
/**
|
|
@@ -208,18 +211,17 @@ class ControllerSetup {
|
|
|
208
211
|
* Disables all admin instances in the objects DB
|
|
209
212
|
* @param objects The contents of objects.json
|
|
210
213
|
*/
|
|
211
|
-
async disableAdminInstances() {
|
|
214
|
+
async disableAdminInstances(dbConnection) {
|
|
212
215
|
debug("Disabling admin instances...");
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
216
|
+
const instanceObjects = await dbConnection.getObjectViewAsync("system", "instance", {
|
|
217
|
+
startkey: "system.adapter.admin.",
|
|
218
|
+
endkey: "system.adapter.admin.\u9999",
|
|
219
|
+
});
|
|
220
|
+
for (const { id, value: obj } of instanceObjects.rows) {
|
|
221
|
+
if (obj && obj.common) {
|
|
222
|
+
obj.common.enabled = false;
|
|
223
|
+
await dbConnection.setObject(id, obj);
|
|
221
224
|
}
|
|
222
|
-
await this.dbConnection.writeObjectsDB(objects);
|
|
223
225
|
}
|
|
224
226
|
debug(" => done!");
|
|
225
227
|
}
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
/// <reference types="iobroker" />
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import EventEmitter from "events";
|
|
2
4
|
export declare type ObjectsDB = Record<string, ioBroker.Object>;
|
|
3
5
|
export declare type StatesDB = Record<string, ioBroker.State>;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
export interface DBConnection {
|
|
7
|
+
on(event: "objectChange", handler: ioBroker.ObjectChangeHandler): this;
|
|
8
|
+
on(event: "stateChange", handler: ioBroker.StateChangeHandler): this;
|
|
9
|
+
}
|
|
10
|
+
/** The DB connection capsules access to the states and objects DB */
|
|
11
|
+
export declare class DBConnection extends EventEmitter {
|
|
6
12
|
private appName;
|
|
7
13
|
private testDir;
|
|
8
14
|
/**
|
|
@@ -11,25 +17,43 @@ export declare class DBConnection {
|
|
|
11
17
|
*/
|
|
12
18
|
constructor(appName: string, testDir: string);
|
|
13
19
|
private testDataDir;
|
|
14
|
-
private
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
20
|
+
private testControllerDir;
|
|
21
|
+
private _objectsServer;
|
|
22
|
+
private _statesServer;
|
|
23
|
+
private _objectsClient;
|
|
24
|
+
/** The underlying objects client instance that can be used to access the objects DB */
|
|
25
|
+
get objectsClient(): any;
|
|
26
|
+
private _statesClient;
|
|
27
|
+
/** The underlying states client instance that can be used to access the states DB */
|
|
28
|
+
get statesClient(): any;
|
|
29
|
+
get objectsType(): "file" | "jsonl";
|
|
30
|
+
get objectsPath(): string;
|
|
31
|
+
get statesType(): "file" | "jsonl";
|
|
32
|
+
get statesPath(): string;
|
|
33
|
+
getSystemConfig(): any;
|
|
34
|
+
backup(): Promise<{
|
|
35
|
+
objects: Buffer;
|
|
36
|
+
states: Buffer;
|
|
28
37
|
}>;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
restore(objects: Buffer, states: Buffer): Promise<void>;
|
|
39
|
+
setSystemConfig(systemConfig: any): void;
|
|
40
|
+
private _isRunning;
|
|
41
|
+
get isRunning(): boolean;
|
|
42
|
+
start(): Promise<void>;
|
|
43
|
+
stop(): Promise<void>;
|
|
44
|
+
/** Creates the objects DB and sets up listeners for it */
|
|
45
|
+
private createObjectsDB;
|
|
46
|
+
/** Creates the states DB and sets up listeners for it */
|
|
47
|
+
private createStatesDB;
|
|
48
|
+
readonly getObject: ioBroker.Adapter["getForeignObjectAsync"];
|
|
49
|
+
readonly setObject: ioBroker.Adapter["setForeignObjectAsync"];
|
|
50
|
+
readonly delObject: ioBroker.Adapter["delForeignObjectAsync"];
|
|
51
|
+
readonly getState: ioBroker.Adapter["getForeignStateAsync"];
|
|
52
|
+
readonly setState: ioBroker.Adapter["setForeignStateAsync"];
|
|
53
|
+
readonly delState: ioBroker.Adapter["delForeignStateAsync"];
|
|
54
|
+
subscribeMessage(id: string): void;
|
|
55
|
+
pushMessage(instanceId: string, msg: any, callback: (err: Error | null, id: any) => void): void;
|
|
56
|
+
readonly getObjectViewAsync: ioBroker.Adapter["getObjectViewAsync"];
|
|
57
|
+
getStateIDs(pattern?: string): Promise<string[]>;
|
|
58
|
+
getObjectIDs(pattern?: string): Promise<string[]>;
|
|
35
59
|
}
|
|
@@ -18,72 +18,287 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
18
18
|
__setModuleDefault(result, mod);
|
|
19
19
|
return result;
|
|
20
20
|
};
|
|
21
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
22
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
23
|
+
};
|
|
21
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
25
|
exports.DBConnection = void 0;
|
|
26
|
+
const debug_1 = __importDefault(require("debug"));
|
|
27
|
+
const events_1 = __importDefault(require("events"));
|
|
23
28
|
const fs_extra_1 = require("fs-extra");
|
|
24
29
|
const path = __importStar(require("path"));
|
|
25
30
|
const tools_1 = require("./tools");
|
|
26
|
-
|
|
27
|
-
|
|
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 {
|
|
28
42
|
/**
|
|
29
43
|
* @param appName The branded name of "iobroker"
|
|
30
44
|
* @param testDir The directory the integration tests are executed in
|
|
31
45
|
*/
|
|
32
46
|
constructor(appName, testDir) {
|
|
47
|
+
super();
|
|
33
48
|
this.appName = appName;
|
|
34
49
|
this.testDir = testDir;
|
|
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");
|
|
78
|
+
}
|
|
79
|
+
return this._statesClient.setStateAsync(...args);
|
|
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);
|
|
35
94
|
this.testDataDir = (0, tools_1.getTestDataDir)(appName, testDir);
|
|
36
|
-
this.objectsPath = path.join(this.testDataDir, "objects.json");
|
|
37
|
-
this.statesPath = path.join(this.testDataDir, "states.json");
|
|
38
|
-
}
|
|
39
|
-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
40
|
-
async readObjectsDB() {
|
|
41
|
-
// debug(`reading objects db...`);
|
|
42
|
-
// debug(` dataDir: ${dataDir}`);
|
|
43
|
-
// debug(` objectsPath: ${objectsPath}`);
|
|
44
|
-
if (await (0, fs_extra_1.pathExists)(this.objectsPath)) {
|
|
45
|
-
// debug(` exists: true`);
|
|
46
|
-
return (0, fs_extra_1.readJSON)(this.objectsPath, { encoding: "utf8" });
|
|
47
|
-
}
|
|
48
95
|
}
|
|
49
|
-
|
|
50
|
-
|
|
96
|
+
/** The underlying objects client instance that can be used to access the objects DB */
|
|
97
|
+
get objectsClient() {
|
|
98
|
+
return this._objectsClient;
|
|
99
|
+
}
|
|
100
|
+
/** The underlying states client instance that can be used to access the states DB */
|
|
101
|
+
get statesClient() {
|
|
102
|
+
return this._statesClient;
|
|
103
|
+
}
|
|
104
|
+
get objectsType() {
|
|
105
|
+
return this.getSystemConfig().objects.type;
|
|
106
|
+
}
|
|
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...");
|
|
51
150
|
return;
|
|
52
|
-
|
|
151
|
+
}
|
|
152
|
+
debug("starting DB instances...");
|
|
153
|
+
await this.createObjectsDB();
|
|
154
|
+
await this.createStatesDB();
|
|
155
|
+
this._isRunning = true;
|
|
156
|
+
debug("DB instances started");
|
|
53
157
|
}
|
|
54
|
-
async
|
|
55
|
-
|
|
158
|
+
async stop() {
|
|
159
|
+
var _a, _b, _c, _d;
|
|
160
|
+
if (!this._isRunning) {
|
|
161
|
+
debug("No DB instance is running, nothing to stop...");
|
|
56
162
|
return;
|
|
57
|
-
return (0, fs_extra_1.writeJSON)(this.statesPath, states);
|
|
58
|
-
}
|
|
59
|
-
async readStatesDB() {
|
|
60
|
-
// debug(`reading states db...`);
|
|
61
|
-
// debug(` dataDir: ${dataDir}`);
|
|
62
|
-
// debug(` statesPath: ${statesPath}`);
|
|
63
|
-
if (await (0, fs_extra_1.pathExists)(this.statesPath)) {
|
|
64
|
-
// debug(` exists: true`);
|
|
65
|
-
return (0, fs_extra_1.readJSON)(this.statesPath, { encoding: "utf8" });
|
|
66
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");
|
|
67
176
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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
|
+
});
|
|
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!");
|
|
78
224
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
+
});
|
|
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)));
|
|
87
302
|
}
|
|
88
303
|
}
|
|
89
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;
|
|
@@ -23,7 +23,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
23
23
|
};
|
|
24
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
25
|
exports.TestHarness = void 0;
|
|
26
|
-
/* eslint-disable @typescript-eslint/no-var-requires */
|
|
27
26
|
const async_1 = require("alcalzone-shared/async");
|
|
28
27
|
const objects_1 = require("alcalzone-shared/objects");
|
|
29
28
|
const child_process_1 = require("child_process");
|
|
@@ -31,18 +30,9 @@ const debug_1 = __importDefault(require("debug"));
|
|
|
31
30
|
const events_1 = require("events");
|
|
32
31
|
const path = __importStar(require("path"));
|
|
33
32
|
const adapterTools_1 = require("../../../lib/adapterTools");
|
|
34
|
-
const dbConnection_1 = require("./dbConnection");
|
|
35
33
|
const tools_1 = require("./tools");
|
|
36
34
|
const debug = (0, debug_1.default)("testing:integration:TestHarness");
|
|
37
35
|
const isWindows = /^win/.test(process.platform);
|
|
38
|
-
/** The logger instance for the objects and states DB */
|
|
39
|
-
const logger = {
|
|
40
|
-
silly: console.log,
|
|
41
|
-
debug: console.log,
|
|
42
|
-
info: console.log,
|
|
43
|
-
warn: console.warn,
|
|
44
|
-
error: console.error,
|
|
45
|
-
};
|
|
46
36
|
const fromAdapterID = "system.adapter.test.0";
|
|
47
37
|
/**
|
|
48
38
|
* The test harness capsules the execution of the JS-Controller and the adapter instance and monitors their status.
|
|
@@ -53,10 +43,11 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
53
43
|
* @param adapterDir The root directory of the adapter
|
|
54
44
|
* @param testDir The directory the integration tests are executed in
|
|
55
45
|
*/
|
|
56
|
-
constructor(adapterDir, testDir) {
|
|
46
|
+
constructor(adapterDir, testDir, dbConnection) {
|
|
57
47
|
super();
|
|
58
48
|
this.adapterDir = adapterDir;
|
|
59
49
|
this.testDir = testDir;
|
|
50
|
+
this.dbConnection = dbConnection;
|
|
60
51
|
this.sendToID = 1;
|
|
61
52
|
debug("Creating instance");
|
|
62
53
|
this.adapterName = (0, adapterTools_1.getAdapterName)(this.adapterDir);
|
|
@@ -68,15 +59,26 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
68
59
|
debug(` adapter: ${this.testAdapterDir}`);
|
|
69
60
|
debug(` appName: ${this.appName}`);
|
|
70
61
|
debug(` adapterName: ${this.adapterName}`);
|
|
71
|
-
|
|
62
|
+
dbConnection.on("objectChange", (id, obj) => {
|
|
63
|
+
this.emit("objectChange", id, obj);
|
|
64
|
+
});
|
|
65
|
+
dbConnection.on("stateChange", (id, state) => {
|
|
66
|
+
this.emit("stateChange", id, state);
|
|
67
|
+
});
|
|
72
68
|
}
|
|
73
|
-
/**
|
|
69
|
+
/** Gives direct access to the Objects DB */
|
|
74
70
|
get objects() {
|
|
75
|
-
|
|
71
|
+
if (!this.dbConnection.objectsClient) {
|
|
72
|
+
throw new Error("Objects DB is not running");
|
|
73
|
+
}
|
|
74
|
+
return this.dbConnection.objectsClient;
|
|
76
75
|
}
|
|
77
|
-
/**
|
|
76
|
+
/** Gives direct access to the States DB */
|
|
78
77
|
get states() {
|
|
79
|
-
|
|
78
|
+
if (!this.dbConnection.statesClient) {
|
|
79
|
+
throw new Error("States DB is not running");
|
|
80
|
+
}
|
|
81
|
+
return this.dbConnection.statesClient;
|
|
80
82
|
}
|
|
81
83
|
/** The process the adapter is running in */
|
|
82
84
|
get adapterProcess() {
|
|
@@ -86,68 +88,15 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
86
88
|
get adapterExit() {
|
|
87
89
|
return this._adapterExit;
|
|
88
90
|
}
|
|
89
|
-
/** Creates the objects DB and sets up listeners for it */
|
|
90
|
-
async createObjectsDB() {
|
|
91
|
-
debug("creating objects DB");
|
|
92
|
-
const Objects = require(path.join(this.testControllerDir, "lib/objects/objectsInMemServer"));
|
|
93
|
-
return new Promise((resolve) => {
|
|
94
|
-
this._objects = new Objects({
|
|
95
|
-
connection: {
|
|
96
|
-
type: "file",
|
|
97
|
-
host: "127.0.0.1",
|
|
98
|
-
port: 19001,
|
|
99
|
-
user: "",
|
|
100
|
-
pass: "",
|
|
101
|
-
noFileCache: false,
|
|
102
|
-
connectTimeout: 2000,
|
|
103
|
-
},
|
|
104
|
-
logger,
|
|
105
|
-
connected: () => {
|
|
106
|
-
debug(" => done!");
|
|
107
|
-
this._objects.subscribe("*");
|
|
108
|
-
resolve();
|
|
109
|
-
},
|
|
110
|
-
change: this.emit.bind(this, "objectChange"),
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
/** Creates the states DB and sets up listeners for it */
|
|
115
|
-
async createStatesDB() {
|
|
116
|
-
debug("creating states DB");
|
|
117
|
-
const States = require(path.join(this.testControllerDir, "lib/states/statesInMemServer"));
|
|
118
|
-
return new Promise((resolve) => {
|
|
119
|
-
this._states = new States({
|
|
120
|
-
connection: {
|
|
121
|
-
type: "file",
|
|
122
|
-
host: "127.0.0.1",
|
|
123
|
-
port: 19000,
|
|
124
|
-
options: {
|
|
125
|
-
auth_pass: null,
|
|
126
|
-
retry_max_delay: 15000,
|
|
127
|
-
},
|
|
128
|
-
},
|
|
129
|
-
logger,
|
|
130
|
-
connected: () => {
|
|
131
|
-
debug(" => done!");
|
|
132
|
-
this._states.subscribe("*");
|
|
133
|
-
resolve();
|
|
134
|
-
},
|
|
135
|
-
change: this.emit.bind(this, "stateChange"),
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
}
|
|
139
91
|
/** Checks if the controller instance is running */
|
|
140
92
|
isControllerRunning() {
|
|
141
|
-
|
|
93
|
+
// The "controller instance" is just the databases, so if they are running,
|
|
94
|
+
// the "controller" is.
|
|
95
|
+
return this.dbConnection.isRunning;
|
|
142
96
|
}
|
|
143
97
|
/** Starts the controller instance by creating the databases */
|
|
144
98
|
async startController() {
|
|
145
|
-
|
|
146
|
-
if (this.isControllerRunning())
|
|
147
|
-
throw new Error("The Controller is already running!");
|
|
148
|
-
await this.createObjectsDB();
|
|
149
|
-
await this.createStatesDB();
|
|
150
|
-
debug("controller instance created");
|
|
99
|
+
await this.dbConnection.start();
|
|
151
100
|
}
|
|
152
101
|
/** Stops the controller instance (and the adapter if it is running) */
|
|
153
102
|
async stopController() {
|
|
@@ -158,7 +107,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
158
107
|
// Give the adapter time to stop (as long as configured in the io-package.json)
|
|
159
108
|
let stopTimeout;
|
|
160
109
|
try {
|
|
161
|
-
stopTimeout = (await this.
|
|
110
|
+
stopTimeout = (await this.dbConnection.getObject(`system.adapter.${this.adapterName}.0`)).common.stopTimeout;
|
|
162
111
|
stopTimeout += 1000;
|
|
163
112
|
}
|
|
164
113
|
catch { }
|
|
@@ -176,16 +125,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
176
125
|
else {
|
|
177
126
|
debug("Adapter failed to start - no need to terminate!");
|
|
178
127
|
}
|
|
179
|
-
|
|
180
|
-
if (this._objects) {
|
|
181
|
-
await this._objects.destroy();
|
|
182
|
-
this._objects = null;
|
|
183
|
-
}
|
|
184
|
-
if (this._states) {
|
|
185
|
-
await this._states.destroy();
|
|
186
|
-
this._states = null;
|
|
187
|
-
}
|
|
188
|
-
debug("Controller instance stopped");
|
|
128
|
+
await this.dbConnection.stop();
|
|
189
129
|
}
|
|
190
130
|
/**
|
|
191
131
|
* Starts the adapter in a separate process and monitors its status
|
|
@@ -259,13 +199,14 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
259
199
|
.on("close", onClose)
|
|
260
200
|
.on("exit", onClose);
|
|
261
201
|
// Tell adapter to stop
|
|
262
|
-
|
|
263
|
-
await this.
|
|
202
|
+
try {
|
|
203
|
+
await this.dbConnection.setState(`system.adapter.${this.adapterName}.0.sigKill`, {
|
|
264
204
|
val: -1,
|
|
265
205
|
from: "system.host.testing",
|
|
266
206
|
});
|
|
267
207
|
}
|
|
268
|
-
|
|
208
|
+
catch {
|
|
209
|
+
// DB connection may be closed already, kill the process
|
|
269
210
|
(_a = this._adapterProcess) === null || _a === void 0 ? void 0 : _a.kill("SIGTERM");
|
|
270
211
|
}
|
|
271
212
|
});
|
|
@@ -273,29 +214,27 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
273
214
|
/**
|
|
274
215
|
* Updates the adapter config. The changes can be a subset of the target object
|
|
275
216
|
*/
|
|
276
|
-
async changeAdapterConfig(
|
|
277
|
-
const objects = await this.dbConnection.readObjectsDB();
|
|
217
|
+
async changeAdapterConfig(adapterName, changes) {
|
|
278
218
|
const adapterInstanceId = `system.adapter.${adapterName}.0`;
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
(0, objects_1.extend)(
|
|
282
|
-
await this.dbConnection.
|
|
219
|
+
const obj = await this.dbConnection.getObject(adapterInstanceId);
|
|
220
|
+
if (obj) {
|
|
221
|
+
(0, objects_1.extend)(obj, changes);
|
|
222
|
+
await this.dbConnection.setObject(adapterInstanceId, obj);
|
|
283
223
|
}
|
|
284
224
|
}
|
|
285
225
|
/** Enables the sendTo method */
|
|
286
|
-
enableSendTo() {
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
}, () => {
|
|
292
|
-
this._states.subscribeMessage(fromAdapterID);
|
|
293
|
-
resolve();
|
|
294
|
-
});
|
|
226
|
+
async enableSendTo() {
|
|
227
|
+
await this.dbConnection.setObject(fromAdapterID, {
|
|
228
|
+
type: "instance",
|
|
229
|
+
common: {},
|
|
230
|
+
native: {},
|
|
295
231
|
});
|
|
232
|
+
this.dbConnection.subscribeMessage(fromAdapterID);
|
|
296
233
|
}
|
|
297
234
|
/** Sends a message to an adapter instance */
|
|
298
|
-
sendTo(target, command,
|
|
235
|
+
sendTo(target, command,
|
|
236
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
237
|
+
message, callback) {
|
|
299
238
|
const stateChangedHandler = (id, state) => {
|
|
300
239
|
if (id === `messagebox.${fromAdapterID}`) {
|
|
301
240
|
callback(state.message);
|
|
@@ -303,7 +242,7 @@ class TestHarness extends events_1.EventEmitter {
|
|
|
303
242
|
}
|
|
304
243
|
};
|
|
305
244
|
this.addListener("stateChange", stateChangedHandler);
|
|
306
|
-
this.
|
|
245
|
+
this.dbConnection.pushMessage(`system.adapter.${target}`, {
|
|
307
246
|
command: command,
|
|
308
247
|
message: message,
|
|
309
248
|
from: fromAdapterID,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iobroker/testing",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.4",
|
|
4
4
|
"description": "Shared utilities for adapter and module testing in ioBroker",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"@alcalzone/release-script-plugin-license": "^3.5.0",
|
|
43
43
|
"@iobroker/adapter-core": "^2.5.1",
|
|
44
44
|
"@tsconfig/node12": "^1.0.9",
|
|
45
|
-
"@types/chai": "^4.
|
|
45
|
+
"@types/chai": "^4.3.0",
|
|
46
46
|
"@types/chai-as-promised": "^7.1.4",
|
|
47
47
|
"@types/debug": "4.1.7",
|
|
48
48
|
"@types/fs-extra": "^9.0.13",
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"eslint": "^7.32.0",
|
|
57
57
|
"eslint-config-prettier": "^8.3.0",
|
|
58
58
|
"eslint-plugin-prettier": "^4.0.0",
|
|
59
|
-
"prettier": "^2.
|
|
59
|
+
"prettier": "^2.5.1",
|
|
60
60
|
"rimraf": "^3.0.2",
|
|
61
61
|
"source-map-support": "^0.5.21",
|
|
62
62
|
"ts-node": "^10.2.1",
|
|
63
|
-
"typescript": "~4.
|
|
63
|
+
"typescript": "~4.5.5"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
66
|
"alcalzone-shared": "~4.0.1",
|