From 975e9af85588c599f9bcf5770fced1bbf31011ff Mon Sep 17 00:00:00 2001 From: TheSomeMan Date: Tue, 23 Apr 2024 12:29:35 +0700 Subject: [PATCH] Update integration tests --- package-lock.json | 10 ++ package.json | 1 + scripts/http_server_auth.py | 3 + scripts/ruuvi_gw_ui_script.js | 124 +++++++++++++++++- tests/test_http.yaml | 8 +- tests/test_http_with_auth.yaml | 14 +- ..._with_server_and_client_cert_checking.yaml | 14 +- .../test_https_with_server_cert_checking.yaml | 12 +- tests/test_mqtt_aws.yaml | 4 +- tests/test_mqtt_ssl.yaml | 4 +- tests/test_mqtt_tcp.yaml | 4 +- tests/test_mqtt_ws.yaml | 4 +- tests/test_mqtt_wss.yaml | 4 +- 13 files changed, 172 insertions(+), 34 deletions(-) diff --git a/package-lock.json b/package-lock.json index 648d347..d568a6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,6 +35,7 @@ "sass-loader": "^13.2.0", "sinon": "^15.0.2", "sinon-chai": "^3.7.0", + "string-argv": "^0.3.2", "style-loader": "^3.3.1", "webpack": "^5.76.0", "webpack-cli": "^5.0.1", @@ -5799,6 +5800,15 @@ "safe-buffer": "~5.2.0" } }, + "node_modules/string-argv": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz", + "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==", + "dev": true, + "engines": { + "node": ">=0.6.19" + } + }, "node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", diff --git a/package.json b/package.json index ca5a478..0715cc8 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "sass-loader": "^13.2.0", "sinon": "^15.0.2", "sinon-chai": "^3.7.0", + "string-argv": "^0.3.2", "style-loader": "^3.3.1", "webpack": "^5.76.0", "webpack-cli": "^5.0.1", diff --git a/scripts/http_server_auth.py b/scripts/http_server_auth.py index 736bb75..bd3e518 100755 --- a/scripts/http_server_auth.py +++ b/scripts/http_server_auth.py @@ -26,11 +26,14 @@ def log(msg): def create_ssl_context(cert_file, key_file, ca_file): context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) if cert_file == key_file: + log(f'Loading cert file {cert_file} as both cert and key file.') context.load_cert_chain(cert_file) else: + log(f'Loading cert file {cert_file} and key file {key_file}.') context.load_cert_chain(cert_file, key_file) if ca_file is not None: + log(f'Loading CA file {ca_file}.') context.load_verify_locations(ca_file) context.verify_mode = ssl.CERT_REQUIRED return context diff --git a/scripts/ruuvi_gw_ui_script.js b/scripts/ruuvi_gw_ui_script.js index e972c1f..11df62d 100644 --- a/scripts/ruuvi_gw_ui_script.js +++ b/scripts/ruuvi_gw_ui_script.js @@ -9,7 +9,10 @@ import path from "path"; import fetch from 'node-fetch'; import https from 'https'; import logger from './ruuvi_gw_ui_logger.js'; -import {exec} from 'child_process'; +import {exec, spawn} from 'child_process'; +import parseArgs from 'string-argv'; + +let array_of_spawned_processes = []; /** Delay execution for the specified number of milliseconds * @@ -389,6 +392,7 @@ export class UiScriptActionDo extends UiScriptAction { HTTP_GET: "httpGet", DOWNLOAD_HISTORY: "downloadHistory", EXEC: "exec", + SPAWN: "spawn", }; /** @@ -474,6 +478,9 @@ export class UiScriptActionDo extends UiScriptAction { if (action_type === UiScriptActionDo.UiScriptActionDoType.EXEC) { return new UiScriptActionDoExec(action_type, args, params); } + if (action_type === UiScriptActionDo.UiScriptActionDoType.SPAWN) { + return new UiScriptActionDoSpawn(action_type, args, params); + } throw new Error(`Invalid 'do' action: ${action_type}`); } @@ -527,13 +534,23 @@ export class UiScriptActionDoFillInput extends UiScriptActionDo { await page.focus(this.selector); // Select all text in the input element - await page.keyboard.down('Control'); + if (process.platform === 'darwin') { + await page.keyboard.down('Meta'); + } else { + await page.keyboard.down('Control'); + } await page.keyboard.press('KeyA'); - await page.keyboard.up('Control'); - + if (process.platform === 'darwin') { + await page.keyboard.up('Meta'); + } else { + await page.keyboard.up('Control'); + } // Press 'Backspace' to delete all selected text await page.keyboard.press('Backspace'); + // Clears the input field + await page.$eval(this.selector, (el) => el.value = ''); + await page.type(this.selector, this.value); await delay(this.postClickDelay, "Post click delay"); @@ -1269,6 +1286,100 @@ export class UiScriptActionDoExec extends UiScriptActionDo { } } +class ProcessManager { + constructor(cmd_line) { + const cmd_line_arr = parseArgs(cmd_line); + this.cmd = cmd_line_arr[0]; + this.cmdArgs = cmd_line_arr.slice(1); + this.processRunning = false; + this.command = null; + } + + runCommandInBackground() { + logger.info(`Spawn: '${this.cmd}' with args: ${this.cmdArgs}`); + + this.command = spawn(this.cmd, this.cmdArgs, { + detached: false, + // stdio: 'ignore' + }); + this.processRunning = true; + logger.info(`Started command in background with pid: ${this.command.pid}`); + + this.command.on('error', (error) => { + logger.error(`Spawn failed with error: ${error.message}`); + }); + + // Listens for the 'exit' event. + this.command.on('exit', (exitCode) => { + logger.info(`Command with PID ${this.command.pid} has finished with exit code: ${exitCode}`); + this.processRunning = false; + this.exitCode = exitCode; + }) + + this.command.stdout.on('data', (data) => { + logger.info(`[PID:${this.command.pid}] stdout: ${data}`); + }); + + this.command.stderr.on('data', (data) => { + logger.error(`[PID:${this.command.pid}] stderr: ${data}`); + }); + + // Now, you can kill the process like this: + // command.kill(); + + // To kill the process with 'Ctrl+C' signal: + // command.kill('SIGINT'); + // Keep in mind that this will only work if the child process is not independent (meaning detached is not set to true and unref method is not called). + } + + isProcessRunning() { + return this.processRunning; + } + + killProcess() { + if (this.command && this.isProcessRunning()) { + logger.info(`Kill process with PID: ${this.command.pid}`); + this.command.kill(); + } + } +} + +/** + * @class + * @extends UiScriptActionDo + */ +export class UiScriptActionDoSpawn extends UiScriptActionDo { + /** + * @param {string} action_type + * @param {string[]} args + * @param {Object | undefined} params + */ + constructor(action_type, args, params) { + super(action_type, params); + if (args.length !== 1) { + throw new Error(`UiScriptActionDoSpawn: Expected 1 argument, got ${args.length}, args: ${args}`); + } + this.cmd_line = args[0]; + this.process = new ProcessManager(this.cmd_line) + } + + /** + * @param {Browser} browser + * @param {Page} page + * @returns {Promise} + */ + async execute(browser, page) { + logger.info(`Execute sequence: Spawn cmd: '${this.cmd_line}'`); + this.process.runCommandInBackground(); + array_of_spawned_processes.push(this.process); + await delay(1000); + if (!this.process.isProcessRunning()) { + throw new Error(`The spawned process exited with exit code: ${this.process.exitCode}`); + } + } + +} + /** * @class */ @@ -1629,7 +1740,7 @@ export class UiScript { if (subst_obj[name] !== undefined) { return subst_obj[name]; } - throw new Error(`Placeholder not found for ${match}`); + throw new Error(`Placeholder is not found for ${match}`); }); } }); @@ -1657,6 +1768,9 @@ export class UiScript { for (const page_obj of this.pages) { await page_obj.execute(browser, page); } + for (const process of array_of_spawned_processes) { + process.killProcess(); + } await browser.close(); } diff --git a/tests/test_http.yaml b/tests/test_http.yaml index eab9cfb..1620502 100644 --- a/tests/test_http.yaml +++ b/tests/test_http.yaml @@ -1,9 +1,9 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" + http_port: 8000 + http_server: "http://${secrets:hostname}:${env:http_port}/record" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 @@ -11,6 +11,8 @@ env: pages: - page?: "#page-auth" steps: + - steps: + - do: spawn "python3 scripts/http_server_auth.py --port "${env:http_port}"" - if: isInvisible "#auth-user_login" then: fail "Remote access is disabled" - if: isEnabled "#auth-user" diff --git a/tests/test_http_with_auth.yaml b/tests/test_http_with_auth.yaml index da5fe47..fbd806b 100644 --- a/tests/test_http_with_auth.yaml +++ b/tests/test_http_with_auth.yaml @@ -1,9 +1,11 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" + http_port: 8000 + http_server: "http://${secrets:hostname}:${env:http_port}/record" + http_auth_user: "user1" + http_auth_pass: "Qe1_z@3Rt1.j" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 @@ -11,6 +13,8 @@ env: pages: - page?: "#page-auth" steps: + - steps: + - do: spawn "python3 scripts/http_server_auth.py --port "${env:http_port}" -u "${env:http_auth_user}" -p "${env:http_auth_pass}"" - if: isInvisible "#auth-user_login" then: fail "Remote access is disabled" - if: isEnabled "#auth-user" @@ -76,14 +80,14 @@ pages: - do: checkCheckbox "#use_http_auth" # Check checkbox 'Use authentication' - do: fillInput "#http_user" "user1" # Fill the input 'Username' - - do: fillInput "#http_pass" "Qe1_z@3Rt1.incorrect" # Fill the input 'Password' + - do: fillInput "#http_pass" "incorrect_password.137!" # Fill the input 'Password' - do: clickButton "#page-custom_server-button-check" # Click on button 'Check' - do: waitUntilLoaded 20000 # Wait until checking is done - if: isInvisible "#page-custom_server-http_validation_error" then: fail "HTTP server check failed" - - do: fillInput "#http_pass" "Qe1_z@3Rt1.j" # Fill the input 'Password' + - do: fillInput "#http_pass" "${env:http_auth_pass}" # Fill the input 'Password' - do: clickButton "#page-custom_server-button-check" # Click on button 'Check' - do: waitUntilLoaded 20000 # Wait until checking is done diff --git a/tests/test_https_with_server_and_client_cert_checking.yaml b/tests/test_https_with_server_and_client_cert_checking.yaml index 87e3dfd..db254a4 100644 --- a/tests/test_https_with_server_and_client_cert_checking.yaml +++ b/tests/test_https_with_server_and_client_cert_checking.yaml @@ -1,9 +1,9 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" + https_port: 8001 + https_server: "https://${secrets:hostname}:${env:https_port}/record" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 @@ -11,6 +11,14 @@ env: pages: - page?: "#page-auth" steps: + - steps: + - do: exec "openssl genrsa -out ${dir:test}/server_key.pem 2048" 5 + - do: exec "openssl req -new -key ${dir:test}/server_key.pem -out ${dir:test}/server_csr.pem -subj "/C=FI/ST=Uusimaa/L=Helsinki/O=Ruuvi/OU=/CN=${secrets:hostname}"" 5 + - do: exec "openssl x509 -req -in ${dir:test}/server_csr.pem -signkey ${dir:test}/server_key.pem -out ${dir:test}/server_cert.pem -days 365" 5 + - do: exec "openssl genrsa -out ${dir:test}/client_key.pem 2048" 5 + - do: exec "openssl req -new -key ${dir:test}/client_key.pem -out ${dir:test}/client_csr.pem -subj "/C=FI/ST=Uusimaa/L=Helsinki/O=Ruuvi/OU=/CN=${secrets:gw_hostname}"" 5 + - do: exec "openssl x509 -req -in ${dir:test}/client_csr.pem -signkey ${dir:test}/client_key.pem -out ${dir:test}/client_cert.pem -days 365" 5 + - do: spawn "python3 scripts/http_server_auth.py --port "${env:https_port}" --ssl_cert "${dir:test}/server_cert.pem" --ssl_key "${dir:test}/server_key.pem" --ca_cert "${dir:test}/client_cert.pem"" - if: isInvisible "#auth-user_login" then: fail "Remote access is disabled" - if: isEnabled "#auth-user" diff --git a/tests/test_https_with_server_cert_checking.yaml b/tests/test_https_with_server_cert_checking.yaml index 208e984..47813dd 100644 --- a/tests/test_https_with_server_cert_checking.yaml +++ b/tests/test_https_with_server_cert_checking.yaml @@ -1,9 +1,9 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" + https_port: 8001 + https_server: "https://${secrets:hostname}:${env:https_port}/record" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 @@ -11,6 +11,12 @@ env: pages: - page?: "#page-auth" steps: + - steps: + - do: exec "openssl genrsa -out ${dir:test}/server_key.pem 2048" 5 + - do: exec "openssl req -new -key ${dir:test}/server_key.pem -out ${dir:test}/server_csr.pem -subj "/C=FI/ST=Uusimaa/L=Helsinki/O=Ruuvi/OU=/CN=${secrets:hostname}"" 5 + - do: exec "openssl x509 -req -in ${dir:test}/server_csr.pem -signkey ${dir:test}/server_key.pem -out ${dir:test}/server_cert.pem -days 365" 5 + - do: delay 1000 + - do: spawn "python3 scripts/http_server_auth.py --port "${env:https_port}" --ssl_cert "${dir:test}/server_cert.pem" --ssl_key "${dir:test}/server_key.pem"" - if: isInvisible "#auth-user_login" then: fail "Remote access is disabled" - if: isEnabled "#auth-user" diff --git a/tests/test_mqtt_aws.yaml b/tests/test_mqtt_aws.yaml index 093cb28..c3e17f4 100644 --- a/tests/test_mqtt_aws.yaml +++ b/tests/test_mqtt_aws.yaml @@ -1,9 +1,7 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 diff --git a/tests/test_mqtt_ssl.yaml b/tests/test_mqtt_ssl.yaml index e00cf78..8a56a20 100644 --- a/tests/test_mqtt_ssl.yaml +++ b/tests/test_mqtt_ssl.yaml @@ -1,9 +1,7 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 diff --git a/tests/test_mqtt_tcp.yaml b/tests/test_mqtt_tcp.yaml index dfefafc..452922f 100644 --- a/tests/test_mqtt_tcp.yaml +++ b/tests/test_mqtt_tcp.yaml @@ -1,9 +1,7 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 diff --git a/tests/test_mqtt_ws.yaml b/tests/test_mqtt_ws.yaml index ec1662a..f0d3972 100644 --- a/tests/test_mqtt_ws.yaml +++ b/tests/test_mqtt_ws.yaml @@ -1,9 +1,7 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500 diff --git a/tests/test_mqtt_wss.yaml b/tests/test_mqtt_wss.yaml index 8f91943..4659911 100644 --- a/tests/test_mqtt_wss.yaml +++ b/tests/test_mqtt_wss.yaml @@ -1,9 +1,7 @@ env: - url: "${secrets:url}" + url: "http://${secrets:gw_hostname}" gw_id: "${secrets:gw_id}" gw_mac: "${secrets:gw_mac}" - http_server: "${secrets:http_server}" - https_server: "${secrets:https_server}" navigationTimeout: 3000 preClickDelay: 1000 postClickDelay: 1500