From 4ce5b0e6456473f30b3ba2b1495d7bfc67ba05b9 Mon Sep 17 00:00:00 2001 From: James Read Date: Thu, 28 Dec 2023 22:04:47 +0000 Subject: [PATCH] cicd: Improve tests (#205) * cicd: make it easier to grab snapshot builds * cicd: Better support for running tests against VMs * Update multipleDropdowns.js --- integration-tests/.mocharc.yml | 1 + integration-tests/Makefile | 5 ++ integration-tests/README.md | 13 ++++ integration-tests/Vagrantfile | 8 ++- integration-tests/envVagrant.sh | 8 +++ integration-tests/mochaSetup.mjs | 2 +- integration-tests/runner.mjs | 75 ++++++++++++++++----- integration-tests/test/general.mjs | 6 +- integration-tests/test/hiddenFooter.mjs | 2 +- integration-tests/test/hiddenNav.mjs | 2 +- integration-tests/test/multipleDropdowns.js | 3 +- 11 files changed, 100 insertions(+), 25 deletions(-) create mode 100755 integration-tests/envVagrant.sh diff --git a/integration-tests/.mocharc.yml b/integration-tests/.mocharc.yml index 012a0bb..53e1f3b 100644 --- a/integration-tests/.mocharc.yml +++ b/integration-tests/.mocharc.yml @@ -1,2 +1,3 @@ +--- require: - mochaSetup.mjs diff --git a/integration-tests/Makefile b/integration-tests/Makefile index 7101c87..c04c1d3 100644 --- a/integration-tests/Makefile +++ b/integration-tests/Makefile @@ -2,4 +2,9 @@ default: npm install --no-fund ./node_modules/.bin/mocha + +getsnapshot: + rm -rf /opt/OliveTin-snapshot/* + gh run download -D /opt/OliveTin-snapshot/ + .PHONY: default diff --git a/integration-tests/README.md b/integration-tests/README.md index 491677f..d16e40f 100644 --- a/integration-tests/README.md +++ b/integration-tests/README.md @@ -1 +1,14 @@ # OliveTin-integration-tests + +## GitHub Actions (Ubuntu, Local Process) + +- `mocha` is run with the default runner that starts and stops OliveTin as a local process (ie, localhost:1337). + +## Running different configurations (Local Process, VM, Container) + +- Get the snapshot you want to test `make getsnapshot` +- To test against VMs: +-- `export OLIVETIN_TEST_RUNNER=container` +-- `vagrant up f38` (or whatever distro you like defined in `Vagrantfile`) +-- `. envVagrant.sh f38` to set the $IP and $PORT +- `mocha` diff --git a/integration-tests/Vagrantfile b/integration-tests/Vagrantfile index e8cf8e8..d36b4f0 100644 --- a/integration-tests/Vagrantfile +++ b/integration-tests/Vagrantfile @@ -5,12 +5,18 @@ Vagrant.configure("2") do |config| config.vm.box = "generic/centos8" config.vm.provision "shell", inline: "mkdir /etc/OliveTin && chmod o+w /etc/OliveTin/", privileged: true - config.vm.provision "file", source: "configs/config.general.yaml/.", destination: "/etc/OliveTin/config.yaml" + config.vm.provision "file", source: "configs/general/config.yaml/.", destination: "/etc/OliveTin/config.yaml" config.vm.provider :libvirt do |libvirt| libvirt.management_network_device = 'virbr0' end + config.vm.define :f38 do |f36| + f36.vm.box = "generic/fedora38" + f36.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.rpm", destination: "$HOME/" + f36.vm.provision "shell", inline: "rpm -U OliveTin* && systemctl enable --now OliveTin && systemctl disable --now firewalld" + end + config.vm.define :f36 do |f36| f36.vm.box = "generic/fedora36" f36.vm.provision "file", source: "/opt/OliveTin-snapshot/OliveTin_linux_amd64.rpm", destination: "$HOME/" diff --git a/integration-tests/envVagrant.sh b/integration-tests/envVagrant.sh new file mode 100755 index 0000000..9f9a5d8 --- /dev/null +++ b/integration-tests/envVagrant.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# Run this like `. envVagrant.sh f38` before `mocha` + +# args: +# $1: The Vagrant VM to test against. If blank and only one VM is provisioned, it will use that. + +export IP=$(vagrant ssh-config $1 | grep HostName | awk '{print $2}') +export PORT=1337 diff --git a/integration-tests/mochaSetup.mjs b/integration-tests/mochaSetup.mjs index 2737578..c0fba28 100644 --- a/integration-tests/mochaSetup.mjs +++ b/integration-tests/mochaSetup.mjs @@ -10,7 +10,7 @@ export async function mochaGlobalSetup () { global.runner = getRunner() - console.log("Runner constructor: " + global.runner.constructor.name) + console.log('Runner constructor: ' + global.runner.constructor.name) } export async function mochaGlobalTeardown () { diff --git a/integration-tests/runner.mjs b/integration-tests/runner.mjs index 14c1774..b31be42 100644 --- a/integration-tests/runner.mjs +++ b/integration-tests/runner.mjs @@ -2,8 +2,6 @@ import process from 'node:process' import waitOn from 'wait-on' import { spawn } from 'node:child_process' -let ot = null - export default function getRunner () { const type = process.env.OLIVETIN_TEST_RUNNER @@ -11,33 +9,41 @@ export default function getRunner () { switch (type) { case 'local': - return new OliveTinTestRunnerLocalProcess() + return new OliveTinTestRunnerStartLocalProcess() case 'vm': - return null + return new OliveTinTestRunnerVm() case 'container': - return null + return new OliveTinTestRunnerEnv() default: - return new OliveTinTestRunnerLocalProcess() + return new OliveTinTestRunnerStartLocalProcess() } } -class OliveTinTestRunnerLocalProcess { +class OliveTinTestRunner { + BASE_URL = 'http://nohost:1337/'; + + baseUrl() { + return this.BASE_URL + } +} + +class OliveTinTestRunnerStartLocalProcess extends OliveTinTestRunner { async start (cfg) { - ot = spawn('./../OliveTin', ['-configdir', 'configs/' + cfg + '/']) + this.ot = spawn('./../OliveTin', ['-configdir', 'configs/' + cfg + '/']) const logStdout = process.env.OLIVETIN_TEST_RUNNER_LOG_STDOUT === '1' if (logStdout) { - ot.stdout.on('data', (data) => { + this.ot.stdout.on('data', (data) => { console.log(`stdout: ${data}`) }) - ot.stderr.on('data', (data) => { + this.ot.stderr.on('data', (data) => { console.error(`stderr: ${data}`) }) } - ot.on('close', (code) => { + this.ot.on('close', (code) => { if (code != null) { console.log(`child process exited with code ${code}`) } @@ -48,14 +54,51 @@ class OliveTinTestRunnerLocalProcess { console.log(`server running on port ${this.server.port}`); */ - await waitOn({ - 'resources': ['http://localhost:1337/'] - }) + this.BASE_URL = 'http://localhost:1337/' - return ot + await waitOn({ + resources: [this.BASE_URL] + }) } async stop () { - await ot.kill() + await this.ot.kill() + } +} + +class OliveTinTestRunnerEnv extends OliveTinTestRunner { + constructor () { + super() + + const IP = process.env.IP + const PORT = process.env.PORT + + this.BASE_URL = 'http://' + IP + ':' + PORT + '/' + + console.log('Runner ENV endpoint: ' + this.BASE_URL) + } + + async start () { + await waitOn({ + resources: [this.BASE_URL] + }) + } + + async stop () { + + } +} + +class OliveTinTestRunnerVm extends OliveTinTestRunnerEnv { + constructor() { + super() + } + + async start (cfg) { + console.log("vagrant changing config") + spawn('vagrant', ['ssh', '-c', '"ln -sf /etc/OliveTin/ /opt/otConfigs/' + cfg + '/"']) + spawn('vagrant', ['ssh', '-c', '"systemctl restart OliveTin"']) + + return null } } diff --git a/integration-tests/test/general.mjs b/integration-tests/test/general.mjs index e067cac..8ccef8d 100644 --- a/integration-tests/test/general.mjs +++ b/integration-tests/test/general.mjs @@ -11,7 +11,7 @@ describe('config: general', function () { }); it('Page title', async function () { - await webdriver.get('http://localhost:1337') + await webdriver.get(runner.baseUrl()) let title = await webdriver.getTitle(); expect(title).to.be.equal("OliveTin") @@ -24,12 +24,12 @@ describe('config: general', function () { }) it('Default buttons are rendered', async function() { - await webdriver.get('http://localhost:1337') + await webdriver.get(runner.baseUrl()) // await webdriver.manage().setTimeouts({ implicit: 2000 }); let buttons = await webdriver.findElement(By.id('root-group')).findElements(By.tagName('button')) - expect(buttons).to.have.length(6); + expect(buttons).to.have.length(6) }) }) diff --git a/integration-tests/test/hiddenFooter.mjs b/integration-tests/test/hiddenFooter.mjs index 26e4130..23c45ed 100644 --- a/integration-tests/test/hiddenFooter.mjs +++ b/integration-tests/test/hiddenFooter.mjs @@ -12,7 +12,7 @@ describe('config: hiddenFooter', function () { }); it('Check that footer is hidden', async () => { - await webdriver.get('http://localhost:1337') + await webdriver.get(runner.baseUrl()) let footer = await webdriver.findElement(By.tagName('footer')) diff --git a/integration-tests/test/hiddenNav.mjs b/integration-tests/test/hiddenNav.mjs index f5ec66a..4b1340b 100644 --- a/integration-tests/test/hiddenNav.mjs +++ b/integration-tests/test/hiddenNav.mjs @@ -11,7 +11,7 @@ describe('config: hiddenNav', function () { }) it('nav is hidden', async () => { - await webdriver.get('http://localhost:1337') + await webdriver.get(runner.baseUrl()) const toggler = await webdriver.findElement(By.id('sidebar-toggle-wrapper')) diff --git a/integration-tests/test/multipleDropdowns.js b/integration-tests/test/multipleDropdowns.js index 82269ea..5dd344d 100644 --- a/integration-tests/test/multipleDropdowns.js +++ b/integration-tests/test/multipleDropdowns.js @@ -1,6 +1,5 @@ import { expect } from 'chai' import { By, until } from 'selenium-webdriver' -import fs from 'node:fs' describe('config: multipleDropdowns', function () { before(async function () { @@ -12,7 +11,7 @@ describe('config: multipleDropdowns', function () { }) it('Multiple dropdowns are possible', async function() { - await webdriver.get('http://localhost:1337') + await webdriver.get(runner.baseUrl()) await webdriver.manage().setTimeouts({ implicit: 2000 }); const button = await webdriver.findElement(By.id('actionButton_bdc45101bbd12c1397557790d9f3e059')).findElement(By.tagName('button'));