add system tests

This commit is contained in:
nachoparker 2018-10-11 22:44:54 -06:00
parent c7455c433c
commit ef3787bb1a
10 changed files with 562 additions and 150 deletions

View File

@ -9,6 +9,7 @@ services:
- "4443:4443"
volumes:
- ncdata:/data
container_name: nextcloudpi
volumes:
ncdata:

View File

@ -8,6 +8,7 @@ services:
- "443:443"
volumes:
- ncdata:/data
container_name: nextcloudpi
volumes:
ncdata:

View File

@ -8,6 +8,7 @@ services:
- "443:443"
volumes:
- ncdata:/data
container_name: nextcloudpi
volumes:
ncdata:

View File

@ -12,6 +12,7 @@ services:
- ./etc:/usr/local/etc
- ./changelog.md:/changelog.md
- ncdata:/data
container_name: nextcloudpi
volumes:
ncdata:

View File

@ -9,6 +9,7 @@ services:
- "4443:4443"
volumes:
- ncdata:/data
container_name: nextcloudpi
volumes:
ncdata:

View File

@ -26,6 +26,9 @@ configure()
ln -s /data/etc/shadow /etc/shadow
}
# Run cron.php once now to get all checks right in CI.
sudo -u www-data php /var/www/nextcloud/cron.php
# activate NCP
a2ensite ncp nextcloud
a2dissite ncp-activation

180
tests/activation_tests.py Executable file
View File

@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""
Automatic testing for NextCloudPi
Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
GPL licensed (see LICENSE file in repository root).
Use at your own risk!
./activation_tests.py [IP]
More at https://ownyourbits.com
"""
import sys
import time
import urllib
import os
import getopt
import configparser
import signal
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from selenium.common.exceptions import UnexpectedAlertPresentException
from selenium.common.exceptions import NoSuchElementException
from selenium.common.exceptions import TimeoutException
suite_name = "activation tests"
test_cfg = 'test_cfg.txt'
test_log = 'test_log.txt'
class tc:
"terminal colors"
brown='\033[33m'
yellow='\033[33;1m'
green='\033[32m'
red='\033[31m'
normal='\033[0m'
def usage():
"Print usage"
print("usage: activation_tests.py [ip]")
class Test:
title = "test"
result = True
def new(self, title):
self.title = title
print("[check] " + "{:16}".format(title), end=' ', flush = True)
def check(self, expression):
if expression:
print(tc.green + "ok" + tc.normal)
self.log("ok")
else:
print(tc.red + "error" + tc.normal)
self.log("error")
sys.exit(1)
def report(self, title, expression):
self.new(title)
self.check(expression)
def log(self, result):
config = configparser.ConfigParser()
if os.path.exists(test_log):
config.read(test_log)
if not config.has_section(suite_name):
config[suite_name] = {}
config[suite_name][self.title] = result
with open(test_log, 'w') as logfile:
config.write(logfile)
def is_element_present(driver, how, what):
try: driver.find_element(by=how, value=what)
except NoSuchElementException: return False
return True
def signal_handler(sig, frame):
sys.exit(0)
def test_activation(IP):
""" Activation process checks"""
# activation page
test = Test()
driver = webdriver.Firefox(service_log_path='/dev/null')
driver.implicitly_wait(5)
test.new("activation opens")
driver.get("https://" + IP)
test.check("NextCloudPi Activation" in driver.title)
try:
ncp_pass = driver.find_element_by_id("ncp-pwd").get_attribute("value")
nc_pass = driver.find_element_by_id("nc-pwd").get_attribute("value")
config = configparser.ConfigParser()
if not config.has_section('credentials'):
config['credentials'] = {}
config['credentials']['ncp_user' ] = 'ncp'
config['credentials']['ncp_pass' ] = ncp_pass
config['credentials']['nc_user' ] = 'ncp'
config['credentials']['nc_pass' ] = nc_pass
with open(test_cfg, 'w') as configfile:
config.write(configfile)
driver.find_element_by_id("activate-ncp").click()
test.report("activation click", True)
except:
ncp_pass = ""
test.report("activation click", False)
test.new("activation ends")
try:
wait = WebDriverWait(driver, 60)
wait.until(EC.text_to_be_present_in_element((By.ID,'error-box'), "ACTIVATION SUCCESSFUL"))
test.check(True)
except TimeoutException:
test.check(False)
except:
test.check(True)
try: driver.close()
except: pass
# ncp-web
test.new("ncp-web")
driver = webdriver.Firefox(service_log_path='/dev/null')
try:
driver.get("https://ncp:" + urllib.parse.quote_plus(ncp_pass) + "@" + IP + ":4443")
except UnexpectedAlertPresentException:
pass
test.check("NextCloudPi Panel" in driver.title)
test.report("first run wizard", is_element_present(driver, By.ID, "first-run-wizard"))
driver.close()
return test.result
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
# parse options
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
sys.exit(2)
else:
usage()
sys.exit(2)
# test
IP = args[0] if len(args) > 0 else 'localhost'
print("Activation tests " + tc.yellow + IP + tc.normal)
print("---------------------------")
test_activation(IP)
# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA

180
tests/nextcloud_tests.py Executable file
View File

@ -0,0 +1,180 @@
#!/usr/bin/env python3
"""
Automatic testing for NextCloudPi
Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
GPL licensed (see LICENSE file in repository root).
Use at your own risk!
./nextcloud_tests.py [IP]
More at https://ownyourbits.com
"""
import sys
import time
import urllib
import os
import getopt
import configparser
import signal
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
suite_name = "nextcloud tests"
test_cfg = 'test_cfg.txt'
test_log = 'test_log.txt'
class tc:
"terminal colors"
brown='\033[33m'
yellow='\033[33;1m'
green='\033[32m'
red='\033[31m'
normal='\033[0m'
class Test:
title = "test"
result = True
def new(self, title):
self.title = title
print("[check] " + "{:16}".format(title), end=' ', flush = True)
def check(self, expression):
if expression:
print(tc.green + "ok" + tc.normal)
self.log("ok")
else:
print(tc.red + "error" + tc.normal)
self.log("error")
sys.exit(1)
def report(self, title, expression):
self.new(title)
self.check(expression)
def log(self, result):
config = configparser.ConfigParser()
if os.path.exists(test_log):
config.read(test_log)
if not config.has_section(suite_name):
config[suite_name] = {}
config[suite_name][self.title] = result
with open(test_log, 'w') as logfile:
config.write(logfile)
def usage():
"Print usage"
print("usage: nextcloud_tests.py [--new] [ip]")
print("--new removes saved configuration")
def signal_handler(sig, frame):
sys.exit(0)
def test_nextcloud(IP):
""" Login and assert admin page checks"""
test = Test()
driver = webdriver.Firefox(service_log_path='/dev/null')
driver.implicitly_wait(60)
test.new("nextcloud page")
try:
driver.get("https://" + IP + "/index.php/settings/admin/overview")
except:
test.check(False)
print(tc.red + "error:" + tc.normal + " unable to reach " + tc.yellow + IP + tc.normal)
sys.exit(1)
test.check("NextCloudPi" in driver.title)
trusted_domain_str = "You are accessing the server from an untrusted domain"
test.report("trusted domain", trusted_domain_str not in driver.page_source)
try:
driver.find_element_by_id("user").send_keys(nc_user)
driver.find_element_by_id("password").send_keys(nc_pass)
driver.find_element_by_id("submit").click()
except: pass
test.report("password", "Wrong password" not in driver.page_source)
test.report("settings page", "Settings" in driver.title)
test.new("settings config")
try:
wait = WebDriverWait(driver, 30)
wait.until(EC.visibility_of(driver.find_element_by_class_name("icon-checkmark-white")))
test.check(True)
except:
test.check(False)
driver.close()
return test.result
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
# parse options
try:
opts, args = getopt.getopt(sys.argv[1:], 'hn', ['help'])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
sys.exit(2)
elif opt in ('-n', '--new'):
if os.path.exists(test_cfg):
os.unlink(test_cfg)
else:
usage()
sys.exit(2)
nc_user = False
nc_pass = False
config = configparser.ConfigParser()
if os.path.exists(test_cfg):
config.read(test_cfg)
try:
nc_user = config['credentials']['nc_user']
nc_pass = config['credentials']['nc_pass']
except: pass
if not nc_user or not nc_pass:
nc_user = input("Nextcloud username (empty=ncp): ")
nc_user = "ncp" if nc_user == "" else nc_user
nc_pass = input("Nextcloud " + nc_user + " password (empty=ownyourbits): ")
nc_pass = "ownyourbits" if nc_pass == "" else nc_pass
print("")
if not config.has_section('credentials'):
config['credentials'] = {}
config['credentials']['nc_user' ] = nc_user
config['credentials']['nc_pass' ] = nc_pass
with open(test_cfg, 'w') as configfile:
config.write(configfile)
# test
IP = args[0] if len(args) > 0 else 'localhost'
print("Nextcloud tests " + tc.yellow + IP + tc.normal)
print("---------------------------")
test_nextcloud(IP)
# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA

194
tests/system_tests.py Executable file
View File

@ -0,0 +1,194 @@
#!/usr/bin/env python3
"""
Automatic system testing for NextCloudPi
Copyleft 2018 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
GPL licensed (see LICENSE file in repository root).
Use at your own risk!
./system_tests.py [user@ip]
More at https://ownyourbits.com
"""
pre_cmd = []
import sys
import getopt
import os
import signal
from subprocess import run, PIPE
processes_must_be_running = [
'apache2',
'cron',
'mysqld',
'php-fpm',
'postfix',
'redis-server',
]
binaries_must_be_installed = [
'dialog',
'dnsmasq',
'git',
'letsencrypt',
'noip2',
'rsync',
'ssh',
]
binaries_no_docker = [
'btrfs',
'fail2ban-server',
'udiskie',
'ufw',
'samba',
'wicd-curses',
]
class tc:
"terminal colors"
brown='\033[33m'
yellow='\033[33;1m'
green='\033[32m'
red='\033[31m'
normal='\033[0m'
def usage():
"Print usage"
print("usage: system_tests.py [user@ip]")
def is_running(process):
"check that a process is running"
print("[running] " + tc.brown + "{:16}".format(process) + tc.normal, end=' ')
result = run(pre_cmd + ['pgrep', '-cf', process], stdout=PIPE, stderr=PIPE)
if result.returncode == 0:
print(tc.green + "ok" + tc.normal)
else:
print(tc.red + "error" + tc.normal)
return result.returncode == 0
def check_processes_running(processes):
"check that all processes are running"
ret = True
for process in processes:
if not is_running(process):
ret = False
return ret
def is_installed(binary):
"check that a binary is installed"
print("[install] " + tc.brown + "{:16}".format(binary) + tc.normal, end=' ')
result = run(pre_cmd + ['sudo', 'which', binary], stdout=PIPE, stderr=PIPE)
if result.returncode == 0:
print(tc.green + "ok" + tc.normal)
else:
print(tc.red + "error" + tc.normal)
return result.returncode == 0
def check_binaries_installed(binaries):
"check that all the binaries are installed"
ret = True
for binary in binaries:
if not is_installed(binary):
ret = False
return ret
def signal_handler(sig, frame):
sys.exit(0)
if __name__ == "__main__":
signal.signal(signal.SIGINT, signal_handler)
# parse options
try:
opts, args = getopt.getopt(sys.argv[1:], 'h', ['help'])
except getopt.GetoptError:
usage()
sys.exit(2)
for opt, arg in opts:
if opt in ('-h', '--help'):
usage()
sys.exit(2)
else:
usage()
sys.exit(2)
# parse arguments
ssh_cmd = "ssh root@nextcloudpi.local"
if len(args) > 0:
if '@' in args[0]:
ssh_cmd = "ssh " + args[0]
else:
print(tc.brown + "* Ignoring invalid SSH argument " + tc.yellow + args[0] + tc.normal)
args = []
# detect if we are running this in a NCP instance
try:
dockers_running = run(['docker', 'ps', '--format', '{{.Image}}'], stdout=PIPE).stdout.decode('utf-8')
except:
dockers_running = ''
# local method
if os.path.exists('/usr/local/etc/ncp-baseimage'):
print(tc.brown + "* local NCP instance detected" + tc.normal)
binaries_must_be_installed = binaries_must_be_installed + binaries_no_docker
pre_cmd = []
# docker method
elif 'ownyourbits/nextcloudpi-' in dockers_running:
print( tc.brown + "* local NCP docker instance detected" + tc.normal)
pre_cmd = ['docker', 'exec', '-ti', 'nextcloudpi']
# SSH method
else:
if len(args) == 0:
print( tc.brown + "* No local NCP instance detected, trying SSH with " +
tc.yellow + ssh_cmd + tc.normal + "...")
binaries_must_be_installed = binaries_must_be_installed + binaries_no_docker
pre_cmd = ['ssh', '-o UserKnownHostsFile=/dev/null' , '-o PasswordAuthentication=no',
'-o StrictHostKeyChecking=no', '-o ConnectTimeout=1', ssh_cmd[4:]]
at_char = ssh_cmd.index('@')
ip = ssh_cmd[at_char+1:]
ping_cmd = run(['ping', '-c1', '-w1', ip], stdout=PIPE, stderr=PIPE)
if ping_cmd.returncode != 0:
print(tc.red + "No connectivity to " + tc.yellow + ip + tc.normal)
sys.exit(1)
ssh_test = run(pre_cmd + [':'], stdout=PIPE, stderr=PIPE)
if ssh_test.returncode != 0:
ssh_copy = run(['ssh-copy-id', ssh_cmd[4:]], stderr=PIPE)
if ssh_copy.returncode != 0:
print(tc.red + "SSH connection failed" + tc.normal)
sys.exit(1)
# checks
print("\nNextCloudPi system checks")
print("-------------------------")
running_result = check_processes_running(processes_must_be_running)
install_result = check_binaries_installed(binaries_must_be_installed)
if running_result and install_result:
sys.exit(0)
else:
sys.exit(1)
# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the

View File

@ -1,150 +0,0 @@
#!/usr/bin/env python3
"""
Automatic testing for NextCloudPi
Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
GPL licensed (see LICENSE file in repository root).
Use at your own risk!
./tests.py <IP>
More at https://ownyourbits.com
"""
import unittest
import sys
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
IP = sys.argv[1]
class AdminWebTest(unittest.TestCase):
"""
Log as admin and assert that all internal checks pass ("All checks passed!")
Also checks for correct trusted domain setting
"""
def setUp(self):
self.driver = webdriver.Firefox()
# @unittest.skip("Skipping...")
def test_admin_checks(self):
""" Login and assert admin page checks"""
driver = self.driver
driver.implicitly_wait(150) # first run can be really slow on QEMU
driver.get("https://" + IP + "/index.php/settings/admin")
self.assertIn("NextCloudPi", driver.title)
trusted_domain_str = "You are accessing the server from an untrusted domain"
self.assertNotIn(trusted_domain_str, driver.page_source)
driver.find_element_by_id("user").send_keys("ncp")
driver.find_element_by_id("password").send_keys("ownyourbits")
driver.find_element_by_id("submit").click()
self.assertNotIn("Wrong password", driver.page_source)
wait = WebDriverWait(driver, 800) # first processing of this page is even slower in NC13
wait.until(EC.visibility_of(driver.find_element_by_class_name("icon-checkmark")))
def tearDown(self):
self.driver.close()
class CreateUserTest(unittest.TestCase):
"""
Create a user, then navigate a little bit
"""
def setUp(self):
self.driver = webdriver.Firefox()
@unittest.skip("Skipping...")
def test_user_creation(self):
""" Create user test_user1 """
driver = self.driver
driver.get("https://" + IP + "/index.php/settings/users")
driver.find_element_by_id("user").send_keys("ncp")
driver.find_element_by_id("password").send_keys("ownyourbits")
driver.find_element_by_id("submit").click()
self.assertNotIn("Wrong password", driver.page_source)
wait = WebDriverWait(driver, 150)
wait.until(lambda driver: driver.find_element_by_id("newusername"))
driver.find_element_by_id("newusername").send_keys("test_user1")
driver.find_element_by_id("newuserpassword").send_keys("ownyourbits")
driver.find_element_by_id("newuserpassword").send_keys(Keys.RETURN)
time.sleep(5)
# navigate a little bit
driver.get("https://" + IP + "/index.php/settings/admin")
self.assertIn("NextCloudPi", driver.title)
driver.get("https://" + IP + "/index.php/settings/apps")
self.assertIn("NextCloudPi", driver.title)
def tearDown(self):
self.driver.close()
class LoginNewUserTest(unittest.TestCase):
"""
Login as the newly created user and check that we are in the Files App
"""
def setUp(self):
self.driver = webdriver.Firefox()
@unittest.skip("Skipping...")
def test_user_login(self):
""" Login as test_user1 """
driver = self.driver
driver.implicitly_wait(210) # first run can be really slow on QEMU
driver.get("https://" + IP)
self.assertIn("NextCloudPi", driver.title)
driver.find_element_by_id("user").send_keys("test_user1")
driver.find_element_by_id("password").send_keys("ownyourbits")
driver.find_element_by_id("submit").click()
self.assertNotIn("Wrong password", driver.page_source)
time.sleep(60) # first run can be really slow on QEMU
wait = WebDriverWait(driver, 210)
wait.until(lambda driver: driver.find_element_by_id("fileList"))
# navigate a little bit
driver.get("https://" + IP + "/index.php/settings/personal")
self.assertIn("NextCloudPi", driver.title)
def tearDown(self):
self.driver.close()
if __name__ == "__main__":
if len(sys.argv) != 2:
print("IP argument required")
sys.exit()
unittest.main(argv=['first-arg-is-ignored'], verbosity=2)
# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA 02111-1307 USA