@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
package/CHANGELOG.md
CHANGED
|
@@ -4,7 +4,16 @@
|
|
|
4
4
|
PLACEHOLDER for next version:
|
|
5
5
|
## __WORK IN PROGRESS__
|
|
6
6
|
-->
|
|
7
|
-
## 2.5.
|
|
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.
|
|
10
|
+
|
|
11
|
+
## 2.5.2 (2021-09-18)
|
|
12
|
+
* Fix: `adminUI.config` is now respected for the config UI check and JSON config is allowed too
|
|
13
|
+
* Updated dependencies
|
|
14
|
+
* Modernized build process
|
|
15
|
+
|
|
16
|
+
## 2.5.1 (2021-09-05)
|
|
8
17
|
* We now use the nightly js-controller dev builds instead of GitHub installation
|
|
9
18
|
|
|
10
19
|
## 2.4.4 (2021-03-14)
|
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2022 AlCalzone <d.griesel@gmx.net>
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -18,15 +18,6 @@ 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
|
-
});
|
|
29
|
-
};
|
|
30
21
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
23
|
};
|
|
@@ -37,7 +28,7 @@ const typeguards_1 = require("alcalzone-shared/typeguards");
|
|
|
37
28
|
const debug_1 = __importDefault(require("debug"));
|
|
38
29
|
const fs_extra_1 = require("fs-extra");
|
|
39
30
|
const path = __importStar(require("path"));
|
|
40
|
-
const debug = debug_1.default("testing:unit:adapterTools");
|
|
31
|
+
const debug = (0, debug_1.default)("testing:unit:adapterTools");
|
|
41
32
|
/**
|
|
42
33
|
* Loads an adapter's package.json
|
|
43
34
|
* @param adapterDir The directory the adapter resides in
|
|
@@ -64,32 +55,30 @@ exports.adapterShouldSupportCompactMode = adapterShouldSupportCompactMode;
|
|
|
64
55
|
* Locates an adapter's main file
|
|
65
56
|
* @param adapterDir The directory the adapter resides in
|
|
66
57
|
*/
|
|
67
|
-
function locateAdapterMainFile(adapterDir) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
throw new Error(`The adapter main file was not found in ${adapterDir}`);
|
|
92
|
-
});
|
|
58
|
+
async function locateAdapterMainFile(adapterDir) {
|
|
59
|
+
debug(`locating adapter main file in ${adapterDir}...`);
|
|
60
|
+
const ioPackage = loadIoPackage(adapterDir);
|
|
61
|
+
const npmPackage = loadNpmPackage(adapterDir);
|
|
62
|
+
// First look for the file defined in io-package.json or package.json or use "main.js" as a fallback
|
|
63
|
+
const mainFile = typeof ioPackage.common.main === "string"
|
|
64
|
+
? ioPackage.common.main
|
|
65
|
+
: typeof npmPackage.main === "string"
|
|
66
|
+
? npmPackage.main
|
|
67
|
+
: "main.js";
|
|
68
|
+
let ret = path.join(adapterDir, mainFile);
|
|
69
|
+
debug(` => trying ${ret}`);
|
|
70
|
+
if (await (0, fs_extra_1.pathExists)(ret)) {
|
|
71
|
+
debug(` => found ${mainFile}`);
|
|
72
|
+
return ret;
|
|
73
|
+
}
|
|
74
|
+
// If both don't exist, JS-Controller uses <adapter name>.js as another fallback
|
|
75
|
+
ret = path.join(adapterDir, ioPackage.common.name + ".js");
|
|
76
|
+
debug(` => trying ${ret}`);
|
|
77
|
+
if (await (0, fs_extra_1.pathExists)(ret)) {
|
|
78
|
+
debug(` => found ${mainFile}`);
|
|
79
|
+
return ret;
|
|
80
|
+
}
|
|
81
|
+
throw new Error(`The adapter main file was not found in ${adapterDir}`);
|
|
93
82
|
}
|
|
94
83
|
exports.locateAdapterMainFile = locateAdapterMainFile;
|
|
95
84
|
/**
|
|
@@ -141,12 +130,12 @@ exports.getAdapterFullName = getAdapterFullName;
|
|
|
141
130
|
function getAdapterDependencies(adapterDir) {
|
|
142
131
|
const ioPackage = loadIoPackage(adapterDir);
|
|
143
132
|
const ret = {};
|
|
144
|
-
if (typeguards_1.isArray(ioPackage.common.dependencies)) {
|
|
133
|
+
if ((0, typeguards_1.isArray)(ioPackage.common.dependencies)) {
|
|
145
134
|
for (const dep of ioPackage.common.dependencies) {
|
|
146
135
|
if (typeof dep === "string") {
|
|
147
136
|
ret[dep] = "latest";
|
|
148
137
|
}
|
|
149
|
-
else if (typeguards_1.isObject(dep)) {
|
|
138
|
+
else if ((0, typeguards_1.isObject)(dep)) {
|
|
150
139
|
const key = Object.keys(dep)[0];
|
|
151
140
|
if (key)
|
|
152
141
|
ret[key] = dep[key] || "latest";
|
|
@@ -7,10 +7,10 @@ const isWindows = /^win/.test(process.platform);
|
|
|
7
7
|
function executeCommand(command, argsOrOptions, options) {
|
|
8
8
|
return new Promise((resolve) => {
|
|
9
9
|
let args;
|
|
10
|
-
if (typeguards_1.isArray(argsOrOptions)) {
|
|
10
|
+
if ((0, typeguards_1.isArray)(argsOrOptions)) {
|
|
11
11
|
args = argsOrOptions;
|
|
12
12
|
}
|
|
13
|
-
else if (typeguards_1.isObject(argsOrOptions)) {
|
|
13
|
+
else if ((0, typeguards_1.isObject)(argsOrOptions)) {
|
|
14
14
|
// no args were given
|
|
15
15
|
options = argsOrOptions;
|
|
16
16
|
}
|
|
@@ -24,7 +24,6 @@ function executeCommand(command, argsOrOptions, options) {
|
|
|
24
24
|
options.stdout || process.stdout,
|
|
25
25
|
options.stderr || process.stderr,
|
|
26
26
|
],
|
|
27
|
-
// @ts-ignore This option exists starting with NodeJS 8
|
|
28
27
|
windowsHide: true,
|
|
29
28
|
};
|
|
30
29
|
if (options.cwd != null)
|
|
@@ -45,7 +44,7 @@ function executeCommand(command, argsOrOptions, options) {
|
|
|
45
44
|
try {
|
|
46
45
|
let bufferedStdout;
|
|
47
46
|
let bufferedStderr;
|
|
48
|
-
const cmd = child_process_1.spawn(command, args, spawnOptions).on("close", (code, signal) => {
|
|
47
|
+
const cmd = (0, child_process_1.spawn)(command, args, spawnOptions).on("close", (code, signal) => {
|
|
49
48
|
resolve({
|
|
50
49
|
exitCode: code !== null && code !== void 0 ? code : undefined,
|
|
51
50
|
signal: signal !== null && signal !== void 0 ? signal : undefined,
|
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 */
|
|
@@ -18,118 +18,101 @@ 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
|
-
});
|
|
29
|
-
};
|
|
30
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
22
|
exports.testAdapter = void 0;
|
|
32
23
|
const async_1 = require("alcalzone-shared/async");
|
|
33
24
|
const os = __importStar(require("os"));
|
|
34
25
|
const path = __importStar(require("path"));
|
|
35
26
|
const adapterTools_1 = require("../../lib/adapterTools");
|
|
36
|
-
const executeCommand_1 = require("../../lib/executeCommand");
|
|
37
27
|
const adapterSetup_1 = require("./lib/adapterSetup");
|
|
38
28
|
const controllerSetup_1 = require("./lib/controllerSetup");
|
|
39
29
|
const dbConnection_1 = require("./lib/dbConnection");
|
|
40
30
|
const harness_1 = require("./lib/harness");
|
|
41
31
|
function testAdapter(adapterDir, options = {}) {
|
|
42
|
-
const appName = adapterTools_1.getAppName(adapterDir);
|
|
43
|
-
const adapterName = adapterTools_1.getAdapterName(adapterDir);
|
|
32
|
+
const appName = (0, adapterTools_1.getAppName)(adapterDir);
|
|
33
|
+
const adapterName = (0, adapterTools_1.getAdapterName)(adapterDir);
|
|
44
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;
|
|
45
37
|
let harness;
|
|
46
|
-
const
|
|
47
|
-
const controllerSetup = new controllerSetup_1.ControllerSetup(adapterDir, testDir, dbConnection);
|
|
38
|
+
const controllerSetup = new controllerSetup_1.ControllerSetup(adapterDir, testDir);
|
|
48
39
|
console.log();
|
|
49
40
|
console.log(`Running tests in ${testDir}`);
|
|
50
41
|
console.log();
|
|
51
42
|
describe(`Test the adapter (in a live environment)`, () => {
|
|
52
43
|
let objectsBackup;
|
|
53
44
|
let statesBackup;
|
|
54
|
-
before(function () {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
// Create a copy of the databases that we can restore later
|
|
80
|
-
({ objects: objectsBackup, states: statesBackup } =
|
|
81
|
-
yield dbConnection.readDB());
|
|
82
|
-
});
|
|
45
|
+
before(async function () {
|
|
46
|
+
// Installation may take a while - especially if rsa-compat needs to be installed
|
|
47
|
+
const oneMinute = 60000;
|
|
48
|
+
this.timeout(30 * oneMinute);
|
|
49
|
+
if (await controllerSetup.isJsControllerRunning()) {
|
|
50
|
+
throw new Error("JS-Controller is already running! Stop it for the first test run and try again!");
|
|
51
|
+
}
|
|
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
|
|
55
|
+
// First we need to copy all files and execute an npm install
|
|
56
|
+
await controllerSetup.prepareTestDir();
|
|
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);
|
|
65
|
+
await adapterSetup.addAdapterInstance();
|
|
66
|
+
await dbConnection.stop();
|
|
67
|
+
// Create a copy of the databases that we can restore later
|
|
68
|
+
({ objects: objectsBackup, states: statesBackup } =
|
|
69
|
+
await dbConnection.backup());
|
|
83
70
|
});
|
|
84
|
-
beforeEach(function () {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
102
|
-
// Start the controller instance
|
|
103
|
-
yield harness.startController();
|
|
104
|
-
// And enable the sendTo emulation
|
|
105
|
-
yield harness.enableSendTo();
|
|
71
|
+
beforeEach(async function () {
|
|
72
|
+
this.timeout(30000);
|
|
73
|
+
dbConnection = new dbConnection_1.DBConnection(appName, testDir);
|
|
74
|
+
// Clean up before every single test
|
|
75
|
+
await Promise.all([
|
|
76
|
+
controllerSetup.clearDBDir(),
|
|
77
|
+
controllerSetup.clearLogDir(),
|
|
78
|
+
dbConnection.restore(objectsBackup, statesBackup),
|
|
79
|
+
]);
|
|
80
|
+
// Create a new test harness
|
|
81
|
+
await dbConnection.start();
|
|
82
|
+
harness = new harness_1.TestHarness(adapterDir, testDir, dbConnection);
|
|
83
|
+
// Enable the adapter and set its loglevel to debug
|
|
84
|
+
await harness.changeAdapterConfig(adapterName, {
|
|
85
|
+
common: {
|
|
86
|
+
enabled: true,
|
|
87
|
+
loglevel: "debug",
|
|
88
|
+
},
|
|
106
89
|
});
|
|
90
|
+
// And enable the sendTo emulation
|
|
91
|
+
await harness.enableSendTo();
|
|
107
92
|
});
|
|
108
|
-
afterEach(function () {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
harness.removeAllListeners();
|
|
115
|
-
});
|
|
93
|
+
afterEach(async function () {
|
|
94
|
+
// Stopping the processes may take a while
|
|
95
|
+
this.timeout(30000);
|
|
96
|
+
// Stop the controller again
|
|
97
|
+
await harness.stopController();
|
|
98
|
+
harness.removeAllListeners();
|
|
116
99
|
});
|
|
117
100
|
it("The adapter starts", function () {
|
|
118
101
|
this.timeout(60000);
|
|
119
102
|
return new Promise((resolve, reject) => {
|
|
120
103
|
// Register a handler to check the alive state and exit codes
|
|
121
104
|
harness
|
|
122
|
-
.on("stateChange", (id, state) =>
|
|
105
|
+
.on("stateChange", async (id, state) => {
|
|
123
106
|
if (id === `system.adapter.${adapterName}.0.alive` &&
|
|
124
107
|
state &&
|
|
125
108
|
state.val === true) {
|
|
126
109
|
// Wait a bit so we can catch errors that do not happen immediately
|
|
127
|
-
|
|
110
|
+
await (0, async_1.wait)(options.waitBeforeStartupSuccess != undefined
|
|
128
111
|
? options.waitBeforeStartupSuccess
|
|
129
112
|
: 5000);
|
|
130
113
|
resolve(`The adapter started successfully.`);
|
|
131
114
|
}
|
|
132
|
-
})
|
|
115
|
+
})
|
|
133
116
|
.on("failed", (code) => {
|
|
134
117
|
if (options.allowedExitCodes == undefined ||
|
|
135
118
|
options.allowedExitCodes.indexOf(code) === -1) {
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { DBConnection } from "./dbConnection";
|
|
1
|
+
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
|
}
|
|
@@ -18,20 +18,12 @@ 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
|
-
});
|
|
29
|
-
};
|
|
30
21
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
31
22
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
32
23
|
};
|
|
33
24
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
34
25
|
exports.AdapterSetup = void 0;
|
|
26
|
+
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
35
27
|
// Add debug logging for tests
|
|
36
28
|
const objects_1 = require("alcalzone-shared/objects");
|
|
37
29
|
const debug_1 = __importDefault(require("debug"));
|
|
@@ -40,18 +32,17 @@ const path = __importStar(require("path"));
|
|
|
40
32
|
const adapterTools_1 = require("../../../lib/adapterTools");
|
|
41
33
|
const executeCommand_1 = require("../../../lib/executeCommand");
|
|
42
34
|
const tools_1 = require("./tools");
|
|
43
|
-
const debug = debug_1.default("testing:integration:AdapterSetup");
|
|
35
|
+
const debug = (0, debug_1.default)("testing:integration:AdapterSetup");
|
|
44
36
|
class AdapterSetup {
|
|
45
|
-
constructor(adapterDir, testDir
|
|
37
|
+
constructor(adapterDir, testDir) {
|
|
46
38
|
this.adapterDir = adapterDir;
|
|
47
39
|
this.testDir = testDir;
|
|
48
|
-
this.dbConnection = dbConnection;
|
|
49
40
|
debug("Creating AdapterSetup...");
|
|
50
|
-
this.adapterName = adapterTools_1.getAdapterName(this.adapterDir);
|
|
51
|
-
this.adapterFullName = adapterTools_1.getAdapterFullName(this.adapterDir);
|
|
52
|
-
this.appName = adapterTools_1.getAppName(this.adapterDir);
|
|
53
|
-
this.testAdapterDir = tools_1.getTestAdapterDir(this.adapterDir, this.testDir);
|
|
54
|
-
this.testControllerDir = tools_1.getTestControllerDir(this.appName, this.testDir);
|
|
41
|
+
this.adapterName = (0, adapterTools_1.getAdapterName)(this.adapterDir);
|
|
42
|
+
this.adapterFullName = (0, adapterTools_1.getAdapterFullName)(this.adapterDir);
|
|
43
|
+
this.appName = (0, adapterTools_1.getAppName)(this.adapterDir);
|
|
44
|
+
this.testAdapterDir = (0, tools_1.getTestAdapterDir)(this.adapterDir, this.testDir);
|
|
45
|
+
this.testControllerDir = (0, tools_1.getTestControllerDir)(this.appName, this.testDir);
|
|
55
46
|
debug(` directories:`);
|
|
56
47
|
debug(` controller: ${this.testControllerDir}`);
|
|
57
48
|
debug(` adapter: ${this.testAdapterDir}`);
|
|
@@ -61,95 +52,88 @@ class AdapterSetup {
|
|
|
61
52
|
/**
|
|
62
53
|
* Tests if the adapter is already installed in the test directory
|
|
63
54
|
*/
|
|
64
|
-
isAdapterInstalled() {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return fs_extra_1.pathExists(this.testAdapterDir);
|
|
68
|
-
});
|
|
55
|
+
async isAdapterInstalled() {
|
|
56
|
+
// We expect the adapter to be installed if the dir in <testDir>/node_modules exists
|
|
57
|
+
return (0, fs_extra_1.pathExists)(this.testAdapterDir);
|
|
69
58
|
}
|
|
70
59
|
/** Copies all adapter files (except a few) to the test directory */
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
stdout: "pipe",
|
|
78
|
-
});
|
|
79
|
-
if (packResult.exitCode !== 0 || typeof packResult.stdout !== "string")
|
|
80
|
-
throw new Error(`Packing the adapter tarball failed!`);
|
|
81
|
-
// The last non-empty line of `npm pack`s STDOUT contains the tarball path
|
|
82
|
-
const stdoutLines = packResult.stdout.trim().split(/[\r\n]+/);
|
|
83
|
-
const tarballName = stdoutLines[stdoutLines.length - 1].trim();
|
|
84
|
-
const tarballPath = path.resolve(this.adapterDir, tarballName);
|
|
85
|
-
yield fs_extra_1.copy(tarballPath, path.resolve(this.testDir, tarballName));
|
|
86
|
-
yield fs_extra_1.unlink(tarballPath);
|
|
87
|
-
// Complete the package.json, so npm can do it's magic
|
|
88
|
-
debug("Saving the adapter in package.json");
|
|
89
|
-
const packageJsonPath = path.join(this.testDir, "package.json");
|
|
90
|
-
const packageJson = yield fs_extra_1.readJSON(packageJsonPath);
|
|
91
|
-
packageJson.dependencies[this.adapterFullName] = `file:./${tarballName}`;
|
|
92
|
-
for (const [dep, version] of objects_1.entries(adapterTools_1.getAdapterDependencies(this.adapterDir))) {
|
|
93
|
-
// Don't overwrite the js-controller github dependency with a probably lower one
|
|
94
|
-
if (dep === "js-controller")
|
|
95
|
-
continue;
|
|
96
|
-
packageJson.dependencies[`${this.appName}.${dep}`] = version;
|
|
97
|
-
}
|
|
98
|
-
yield fs_extra_1.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
|
|
99
|
-
debug("Deleting old remains of this adapter");
|
|
100
|
-
if (yield fs_extra_1.pathExists(this.testAdapterDir))
|
|
101
|
-
yield fs_extra_1.remove(this.testAdapterDir);
|
|
102
|
-
debug(" => done!");
|
|
60
|
+
async installAdapterInTestDir() {
|
|
61
|
+
debug("Copying adapter files to test directory...");
|
|
62
|
+
// We install the adapter almost like it would be installed in the real world
|
|
63
|
+
// Therefore pack it into a tarball and put it in the test dir for installation
|
|
64
|
+
const packResult = await (0, executeCommand_1.executeCommand)("npm", ["pack", "--loglevel", "silent"], {
|
|
65
|
+
stdout: "pipe",
|
|
103
66
|
});
|
|
67
|
+
if (packResult.exitCode !== 0 || typeof packResult.stdout !== "string")
|
|
68
|
+
throw new Error(`Packing the adapter tarball failed!`);
|
|
69
|
+
// The last non-empty line of `npm pack`s STDOUT contains the tarball path
|
|
70
|
+
const stdoutLines = packResult.stdout.trim().split(/[\r\n]+/);
|
|
71
|
+
const tarballName = stdoutLines[stdoutLines.length - 1].trim();
|
|
72
|
+
const tarballPath = path.resolve(this.adapterDir, tarballName);
|
|
73
|
+
await (0, fs_extra_1.copy)(tarballPath, path.resolve(this.testDir, tarballName));
|
|
74
|
+
await (0, fs_extra_1.unlink)(tarballPath);
|
|
75
|
+
// Complete the package.json, so npm can do it's magic
|
|
76
|
+
debug("Saving the adapter in package.json");
|
|
77
|
+
const packageJsonPath = path.join(this.testDir, "package.json");
|
|
78
|
+
const packageJson = await (0, fs_extra_1.readJSON)(packageJsonPath);
|
|
79
|
+
packageJson.dependencies[this.adapterFullName] = `file:./${tarballName}`;
|
|
80
|
+
for (const [dep, version] of (0, objects_1.entries)((0, adapterTools_1.getAdapterDependencies)(this.adapterDir))) {
|
|
81
|
+
// Don't overwrite the js-controller github dependency with a probably lower one
|
|
82
|
+
if (dep === "js-controller")
|
|
83
|
+
continue;
|
|
84
|
+
packageJson.dependencies[`${this.appName}.${dep}`] = version;
|
|
85
|
+
}
|
|
86
|
+
await (0, fs_extra_1.writeJSON)(packageJsonPath, packageJson, { spaces: 2 });
|
|
87
|
+
debug("Deleting old remains of this adapter");
|
|
88
|
+
if (await (0, fs_extra_1.pathExists)(this.testAdapterDir))
|
|
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
|
+
});
|
|
95
|
+
debug(" => done!");
|
|
104
96
|
}
|
|
105
97
|
/**
|
|
106
98
|
* Adds an instance for an already installed adapter in the test directory
|
|
107
99
|
*/
|
|
108
|
-
addAdapterInstance() {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
stdout: "ignore",
|
|
121
|
-
});
|
|
122
|
-
if (addResult.exitCode !== 0)
|
|
123
|
-
throw new Error(`Adding the adapter instance failed!`);
|
|
124
|
-
debug(" => done!");
|
|
100
|
+
async addAdapterInstance() {
|
|
101
|
+
debug("Adding adapter instance...");
|
|
102
|
+
// execute iobroker add <adapter> -- This also installs missing dependencies
|
|
103
|
+
const addResult = await (0, executeCommand_1.executeCommand)("node", [
|
|
104
|
+
`${this.appName}.js`,
|
|
105
|
+
"add",
|
|
106
|
+
this.adapterName,
|
|
107
|
+
"--enabled",
|
|
108
|
+
"false",
|
|
109
|
+
], {
|
|
110
|
+
cwd: this.testControllerDir,
|
|
111
|
+
stdout: "ignore",
|
|
125
112
|
});
|
|
113
|
+
if (addResult.exitCode !== 0)
|
|
114
|
+
throw new Error(`Adding the adapter instance failed!`);
|
|
115
|
+
debug(" => done!");
|
|
126
116
|
}
|
|
127
|
-
deleteOldInstances() {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (belongsToAdapter(id))
|
|
148
|
-
delete states[id];
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
yield this.dbConnection.writeDB(objects, states);
|
|
152
|
-
});
|
|
117
|
+
async deleteOldInstances(dbConnection) {
|
|
118
|
+
debug("Removing old adapter instances...");
|
|
119
|
+
const allKeys = new Set([
|
|
120
|
+
...(await dbConnection.getObjectIDs()),
|
|
121
|
+
...(await dbConnection.getStateIDs()),
|
|
122
|
+
]);
|
|
123
|
+
const instanceRegex = new RegExp(`^system\\.adapter\\.${this.adapterName}\\.\\d+`);
|
|
124
|
+
const instanceObjsRegex = new RegExp(`^${this.adapterName}\\.\\d+\.`);
|
|
125
|
+
const belongsToAdapter = (id) => {
|
|
126
|
+
return (instanceRegex.test(id) ||
|
|
127
|
+
instanceObjsRegex.test(id) ||
|
|
128
|
+
id === this.adapterName ||
|
|
129
|
+
id === `${this.adapterName}.admin`);
|
|
130
|
+
};
|
|
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(() => { });
|
|
135
|
+
}
|
|
136
|
+
debug(" => done!");
|
|
153
137
|
}
|
|
154
138
|
}
|
|
155
139
|
exports.AdapterSetup = AdapterSetup;
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { DBConnection } from "./dbConnection";
|
|
1
|
+
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
|
}
|