diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..324be6ef0 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,24 @@ +run_tests: + stage: test + image: cloudron/base:4.2.0@sha256:46da2fffb36353ef714f97ae8e962bd2c212ca091108d768ba473078319a47f4 + services: + - name: mysql:8.0 + alias: mysql + variables: + MYSQL_ROOT_PASSWORD: password + MYSQL_DATABASE: box + BOX_ENV: ci + DATABASE_URL: mysql://root:password@mysql/box + script: + - echo "Running tests..." + - mysql -hmysql -uroot -ppassword -e "ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';" + - mysql -hmysql -uroot -ppassword -e "CREATE DATABASE IF NOT EXISTS box" + - npm install + - node_modules/.bin/db-migrate up + - ln -s /usr/local/node-18.18.0/bin/node /usr/bin/node + - node_modules/.bin/mocha --no-timeouts --bail src/test/tokens-test.js + - echo "Done!" + +stages: + - test + diff --git a/run-tests b/run-tests index 2631b93dc..c8a1ce04c 100755 --- a/run-tests +++ b/run-tests @@ -2,40 +2,11 @@ set -eu -readonly source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -readonly DATA_DIR="${HOME}/.cloudron_test" readonly DEFAULT_TESTS="./src/test/*-test.js ./src/routes/test/*-test.js" -! "${source_dir}/src/test/check-install" && exit 1 - -# cleanup old data dirs some of those docker container data requires sudo to be removed -echo "=> Provide root password to purge any leftover data in ${DATA_DIR} and load apparmor profile:" -sudo rm -rf ${DATA_DIR} - -# archlinux does not have apparmor -if hash apparmor_parser 2>/dev/null; then - echo "=> Loading app armor profile" - sudo apparmor_parser --replace --write-cache ./setup/start/docker-cloudron-app.apparmor -fi - -# create dir structure -mkdir -p ${DATA_DIR} -cd ${DATA_DIR} -mkdir -p appsdata -mkdir -p boxdata/box boxdata/mail boxdata/certs boxdata/mail/dkim/localhost boxdata/mail/dkim/foobar.com -mkdir -p platformdata/addons/mail/banner platformdata/nginx/cert platformdata/nginx/applications/dashboard platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks platformdata/sftp/ssh platformdata/firewall platformdata/update -sudo mkdir -p /mnt/cloudron-test-music /media/cloudron-test-music # volume test - -# put cert -echo "=> Generating a localhost selfsigned cert" -openssl req -x509 -newkey rsa:2048 -keyout platformdata/nginx/cert/host.key -out platformdata/nginx/cert/host.cert -days 3650 -subj '/CN=localhost' -nodes -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.localhost")) - -cd "${source_dir}" - # clear out any containers if FAST is unset if [[ -z ${FAST+x} ]]; then - echo "=> Delete all docker containers first" - docker ps -qa --filter "label=isCloudronManaged" | xargs --no-run-if-empty docker rm -f + echo "=> Delete mysql server" docker rm -f mysql-server echo "==> To skip this run with: FAST=1 ./run-tests" else @@ -62,11 +33,8 @@ while ! mysqladmin ping -h"${MYSQL_IP}" --silent; do sleep 1 done -echo "=> Ensure local base image" -docker pull cloudron/base:4.2.0@sha256:46da2fffb36353ef714f97ae8e962bd2c212ca091108d768ba473078319a47f4 - -echo "=> Create iptables blocklist" -sudo ipset create cloudron_blocklist hash:net || true +# echo "=> Create iptables blocklist" +# sudo ipset create cloudron_blocklist hash:net || true echo "=> Ensure database" mysql -h"${MYSQL_IP}" -uroot -ppassword -e "ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'password';" @@ -80,5 +48,6 @@ if [[ $# -gt 0 ]]; then TESTS="$*" fi -echo "=> Run tests with mocha" -BOX_ENV=test ./node_modules/.bin/mocha --bail --no-timeouts --exit -R spec ${TESTS} +echo "=> Run tests" +docker run -e BOX_ENV=test -e DATABASE_HOSTNAME=${MYSQL_IP} -v `pwd`:/home/yellowtent/box:ro -v `which node`:/usr/bin/node:ro -t cloudron/boxtest node_modules/.bin/mocha --bail --no-timeouts --colors --exit -R spec src/test + diff --git a/src/applinks.js b/src/applinks.js index 58e4acf2d..23182f7c7 100644 --- a/src/applinks.js +++ b/src/applinks.js @@ -90,7 +90,7 @@ async function detectMetaInfo(applink) { }); const [jsdomError, dom] = await safe(jsdom.JSDOM.fromURL(applink.upstreamUri, { virtualConsole })); - if (jsdomError) console.error('detectMetaInfo: jsdomError', jsdomError); + if (jsdomError) debug('detectMetaInfo: jsdomError', jsdomError); if (!applink.icon && dom) { let favicon = ''; diff --git a/src/database.js b/src/database.js index 0dc80500c..53b438345 100644 --- a/src/database.js +++ b/src/database.js @@ -25,7 +25,7 @@ const assert = require('assert'), let gConnectionPool = null; const gDatabase = { - hostname: '127.0.0.1', + hostname: constants.TEST ? process.env.DATABASE_HOSTNAME : '127.0.0.1', username: 'root', password: 'password', port: 3306, @@ -35,11 +35,6 @@ const gDatabase = { async function initialize() { if (gConnectionPool !== null) return; - if (constants.TEST) { - // see setupTest script how the mysql-server is run - gDatabase.hostname = require('child_process').execSync('docker inspect -f "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}" mysql-server').toString().trim(); - } - // https://github.com/mysqljs/mysql#pool-options gConnectionPool = mysql.createPool({ connectionLimit: 5, diff --git a/src/dockerproxy.js b/src/dockerproxy.js index f70db6e6c..88f368407 100644 --- a/src/dockerproxy.js +++ b/src/dockerproxy.js @@ -118,6 +118,8 @@ function process(req, res, next) { async function start() { assert(gHttpServer === null, 'Already started'); + if (constants.TEST) return; + const json = express.json({ strict: true }); // we protect container create as the app/admin can otherwise mount random paths (like the ghost file) diff --git a/src/paths.js b/src/paths.js index a3e673674..3702a3d74 100644 --- a/src/paths.js +++ b/src/paths.js @@ -4,10 +4,7 @@ const constants = require('./constants.js'), path = require('path'); function baseDir() { - const homeDir = process.env.HOME; - if (constants.CLOUDRON) return homeDir; - if (constants.TEST) return path.join(homeDir, '.cloudron_test'); - // cannot reach + return process.env.HOME; } // keep these values in sync with start.sh diff --git a/src/test/applinks-test.js b/src/test/applinks-test.js index b4aff5044..fbba66c87 100644 --- a/src/test/applinks-test.js +++ b/src/test/applinks-test.js @@ -14,6 +14,8 @@ const applinks = require('../applinks.js'), describe('Applinks', function () { const { setup, cleanup } = common; + this.timeout(10000); + before(setup); after(cleanup); @@ -30,7 +32,7 @@ describe('Applinks', function () { }; const APPLINK_2 = { - upstreamUri: 'https://google.com' + upstreamUri: 'https://google.de' }; const APPLINK_3 = { diff --git a/src/test/check-install b/src/test/check-install deleted file mode 100755 index 1345718f0..000000000 --- a/src/test/check-install +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash - -set -eu - -readonly source_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" -readonly sudo_scripts_dir="${source_dir}/src/scripts" - -if [[ ! -f /usr/bin/node ]]; then - echo "node is not in root user's environment. '/usr/bin/env node' will not work" - exit 1 -fi - -# checks if all scripts are sudo access -readarray -d '' scripts < <(find ${sudo_scripts_dir} -type f -print0) -declare -a missing_scripts=() -for script in "${scripts[@]}"; do - # sudo -k ignores a cached sudo session for the command - if [[ $(sudo -k -n "${script}" --check 2>/dev/null) != "OK" ]]; then - missing_scripts+=("${script}") - fi -done - -if [[ ${#missing_scripts[@]} -gt 0 ]]; then - echo "The following script(s) have no sudo access: ${missing_scripts[*]} . Try 'sudo -n ${missing_scripts[0]} --check'" - echo -e "\nYou have to add the lines below to /etc/sudoers.d/yellowtent\n\n" - - for missing_script in "${missing_scripts[@]}"; do - echo "Defaults!${missing_script} env_keep=\"HOME BOX_ENV\"" - echo "${USER} ALL=(ALL) NOPASSWD: ${missing_script}" - echo "" - done - - exit 1 -fi - -setenv_scripts=(starttask.sh backupupload.js) -for script in "${setenv_scripts[@]}"; do - if ! grep -q ":SETENV:.*${script}" "/etc/sudoers.d/yellowtent"; then - echo "SETENV missing for ${script} in /etc/sudoers.d/yellowtent" - exit 1 - fi -done - -if ! grep -q "backupupload.js closefrom_override" "/etc/sudoers.d/yellowtent"; then - echo "backupupload.js needs closefrom_override in /etc/sudoers.d/yellowtent" - exit 1 -fi - -images=$(node -e "const i = require('${source_dir}/src/infra_version.js'); console.log(Object.keys(i.images).map(x => i.images[x]).join(' '));") - -for image in ${images}; do - if ! docker inspect "${image}" >/dev/null 2>/dev/null; then - docker pull ${image%@sha256:*} - fi -done - diff --git a/src/test/df-test.js b/src/test/df-test.js index 606261d8c..5acb87203 100644 --- a/src/test/df-test.js +++ b/src/test/df-test.js @@ -17,20 +17,14 @@ describe('System', function () { after(cleanup); it('can get disks', async function () { - // does not work on archlinux 8! - if (require('child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; - const disks = await df.disks(); expect(disks).to.be.ok(); expect(disks.some(d => d.mountpoint === '/')).to.be.ok(); }); it('can get file', async function () { - // does not work on archlinux 8! - if (require('child_process').execSync('uname -a').toString().indexOf('-arch') !== -1) return; - const disks = await df.file(__dirname); expect(disks).to.be.ok(); - expect(disks.mountpoint).to.be('/home'); + expect(disks.mountpoint).to.be('/home/yellowtent/box'); }); }); diff --git a/test/Dockerfile b/test/Dockerfile new file mode 100644 index 000000000..f7e7e9014 --- /dev/null +++ b/test/Dockerfile @@ -0,0 +1,26 @@ +FROM ubuntu:jammy-20230816@sha256:b492494d8e0113c4ad3fe4528a4b5ff89faa5331f7d52c5c138196f69ce176a6 + +RUN apt update && \ + apt install -y openssl mysql-client-8.0 sudo lsb-release vim + +RUN useradd --system --uid 808 --comment "Cloudron Box" --create-home --shell /usr/sbin/nologin yellowtent + +RUN mkdir -p /mnt/cloudron-test-music /media/cloudron-test-music # volume test + +COPY setup/start/sudoers /etc/sudoers.d/cloudron + +COPY test/cloak /usr/bin/cloak +RUN ln -s /usr/bin/cloak /usr/bin/systemd-run && \ + ln -s /usr/bin/cloak /usr/bin/systemctl + +WORKDIR /home/yellowtent +USER yellowtent + +RUN mkdir -p appsdata +RUN mkdir -p boxdata/box boxdata/mail boxdata/certs boxdata/mail/dkim/localhost boxdata/mail/dkim/foobar.com +RUN mkdir -p platformdata/addons/mail/banner platformdata/nginx/cert platformdata/nginx/applications/dashboard platformdata/collectd/collectd.conf.d platformdata/addons platformdata/logrotate.d platformdata/backup platformdata/logs/tasks platformdata/sftp/ssh platformdata/firewall platformdata/update + +RUN bash -c 'openssl req -x509 -newkey rsa:2048 -keyout platformdata/nginx/cert/host.key -out platformdata/nginx/cert/host.cert -days 3650 -subj '/CN=localhost' -nodes -config <(cat /etc/ssl/openssl.cnf <(printf "\n[SAN]\nsubjectAltName=DNS:*.localhost"))' + +WORKDIR /home/yellowtent/box + diff --git a/test/cloak b/test/cloak new file mode 100755 index 000000000..86c0450e9 --- /dev/null +++ b/test/cloak @@ -0,0 +1,8 @@ +#!/bin/bash + +set -eu + +cmd=$(basename $BASH_SOURCE) + +# echo $cmd "$@" +