add tests
continuous-integration/drone/pr Build is failing
Details
continuous-integration/drone/pr Build is failing
Details
This commit is contained in:
parent
300688cb9a
commit
df412191e3
|
@ -4,11 +4,11 @@ import os
|
||||||
from zope import component
|
from zope import component
|
||||||
from ceo_common.interfaces import IConfig
|
from ceo_common.interfaces import IConfig
|
||||||
|
|
||||||
from ..utils import http_post, http_get
|
from ..utils import http_post, http_get, http_delete
|
||||||
from .utils import handle_sync_response, check_file_path
|
from .utils import handle_sync_response, check_file_path, check_if_in_development
|
||||||
|
|
||||||
|
|
||||||
def mysql_create_info_file(file, username, password):
|
def mysql_cli_response(file, username, password):
|
||||||
cfg_srv = component.getUtility(IConfig)
|
cfg_srv = component.getUtility(IConfig)
|
||||||
mysql_host = cfg_srv.get('mysql_host')
|
mysql_host = cfg_srv.get('mysql_host')
|
||||||
info = f"""MySQL Database Information for {username}
|
info = f"""MySQL Database Information for {username}
|
||||||
|
@ -34,6 +34,17 @@ def mysql_create_info_file(file, username, password):
|
||||||
os.chown(file, username, username)
|
os.chown(file, username, username)
|
||||||
os.chmod(file, 0o640)
|
os.chmod(file, 0o640)
|
||||||
|
|
||||||
|
click.echo(f"""MySQL database created
|
||||||
|
|
||||||
|
Connection Information:
|
||||||
|
|
||||||
|
Database: {username}
|
||||||
|
Username: {username}
|
||||||
|
Password: {password}
|
||||||
|
Host: {mysql_host}
|
||||||
|
|
||||||
|
Settings and more info has been written to {file}""")
|
||||||
|
|
||||||
|
|
||||||
@click.group(short_help='Perform operations on MySQL')
|
@click.group(short_help='Perform operations on MySQL')
|
||||||
def mysql():
|
def mysql():
|
||||||
|
@ -53,11 +64,7 @@ def create(username):
|
||||||
result = handle_sync_response(resp)
|
result = handle_sync_response(resp)
|
||||||
password = result['password']
|
password = result['password']
|
||||||
|
|
||||||
mysql_create_info_file(info_file_path, username, password)
|
mysql_cli_response(info_file_path, username, password)
|
||||||
|
|
||||||
click.echo(f"""
|
|
||||||
MySQL database {username} with password {password} has been created
|
|
||||||
This password and more details have been written to {info_file_path}""")
|
|
||||||
|
|
||||||
|
|
||||||
@mysql.command(short_help='Reset the password of a MySQL user')
|
@mysql.command(short_help='Reset the password of a MySQL user')
|
||||||
|
@ -73,8 +80,13 @@ def pwreset(username):
|
||||||
result = handle_sync_response(resp)
|
result = handle_sync_response(resp)
|
||||||
password = result['password']
|
password = result['password']
|
||||||
|
|
||||||
mysql_create_info_file(info_file_path, username, password)
|
mysql_cli_response(info_file_path, username, password)
|
||||||
|
|
||||||
click.echo(f"""
|
|
||||||
MySQL database {username} now has the password {password}
|
@mysql.command(short_help="Delete the database of a MySQL user")
|
||||||
This password and more details have been written to {info_file_path}""")
|
@click.argument('username')
|
||||||
|
def delete(username):
|
||||||
|
check_if_in_development()
|
||||||
|
click.confirm(f"Are you sure?", abort=True)
|
||||||
|
resp = http_delete(f'/api/db/mysql/{username}')
|
||||||
|
handle_sync_response(resp)
|
||||||
|
|
|
@ -4,11 +4,11 @@ import os
|
||||||
from zope import component
|
from zope import component
|
||||||
from ceo_common.interfaces import IConfig
|
from ceo_common.interfaces import IConfig
|
||||||
|
|
||||||
from ..utils import http_post, http_get
|
from ..utils import http_post, http_get, http_delete
|
||||||
from .utils import handle_sync_response, check_file_path
|
from .utils import handle_sync_response, check_file_path, check_if_in_development
|
||||||
|
|
||||||
|
|
||||||
def psql_create_info_file(file, username, password):
|
def psql_cli_response(file, username, password):
|
||||||
cfg_srv = component.getUtility(IConfig)
|
cfg_srv = component.getUtility(IConfig)
|
||||||
psql_host = cfg_srv.get('postgresql_host')
|
psql_host = cfg_srv.get('postgresql_host')
|
||||||
info = f"""PostgreSQL Database Information for {username}
|
info = f"""PostgreSQL Database Information for {username}
|
||||||
|
@ -34,6 +34,17 @@ def psql_create_info_file(file, username, password):
|
||||||
os.chown(file, username, username)
|
os.chown(file, username, username)
|
||||||
os.chmod(file, 0o640)
|
os.chmod(file, 0o640)
|
||||||
|
|
||||||
|
click.echo(f"""PostgreSQL database created
|
||||||
|
|
||||||
|
Connection Information:
|
||||||
|
|
||||||
|
Database: {username}
|
||||||
|
Username: {username}
|
||||||
|
Password: {password}
|
||||||
|
Host: {psql_host}
|
||||||
|
|
||||||
|
Settings and more info has been written to {file}""")
|
||||||
|
|
||||||
|
|
||||||
@click.group(short_help='Perform operations on PostgreSQL')
|
@click.group(short_help='Perform operations on PostgreSQL')
|
||||||
def postgresql():
|
def postgresql():
|
||||||
|
@ -53,11 +64,7 @@ def create(username):
|
||||||
result = handle_sync_response(resp)
|
result = handle_sync_response(resp)
|
||||||
password = result['password']
|
password = result['password']
|
||||||
|
|
||||||
psql_create_info_file(info_file_path, username, password)
|
psql_cli_response(info_file_path, username, password)
|
||||||
|
|
||||||
click.echo(f"""
|
|
||||||
PostgreSQL database {username} with password {password} has been created
|
|
||||||
This password and more details have been written to {info_file_path}""")
|
|
||||||
|
|
||||||
|
|
||||||
@postgresql.command(short_help='Reset the password of a PostgreSQL user')
|
@postgresql.command(short_help='Reset the password of a PostgreSQL user')
|
||||||
|
@ -73,8 +80,13 @@ def pwreset(username):
|
||||||
result = handle_sync_response(resp)
|
result = handle_sync_response(resp)
|
||||||
password = result['password']
|
password = result['password']
|
||||||
|
|
||||||
psql_create_info_file(info_file_path, username, password)
|
psql_cli_response(info_file_path, username, password)
|
||||||
|
|
||||||
click.echo(f"""
|
|
||||||
PostgreSQL database {username} now has the password {password}
|
@postgresql.command(short_help="Delete the database of a PostgreSQL user")
|
||||||
This password and more details have been written to {info_file_path}""")
|
@click.argument('username')
|
||||||
|
def delete(username):
|
||||||
|
check_if_in_development()
|
||||||
|
click.confirm(f"Are you sure?", abort=True)
|
||||||
|
resp = http_delete(f'/api/db/postgresql/{username}')
|
||||||
|
handle_sync_response(resp)
|
||||||
|
|
|
@ -31,9 +31,9 @@ def db_exception_handler(func):
|
||||||
except InvalidUsernameError:
|
except InvalidUsernameError:
|
||||||
return {'error': 'username contains invalid characters'}, 400
|
return {'error': 'username contains invalid characters'}, 400
|
||||||
except DatabaseConnectionError:
|
except DatabaseConnectionError:
|
||||||
return {'error': 'unable to connect or authenticate to sql server'}, 500
|
return {'error': 'unable to connect to sql server'}, 500
|
||||||
except DatabasePermissionError:
|
except DatabasePermissionError:
|
||||||
return {'error': 'unable to perform action due to permissions'}, 500
|
return {'error': 'unable to connect or action failed due to permissions'}, 500
|
||||||
return function
|
return function
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -35,9 +35,11 @@ class MySQLService:
|
||||||
password=self.auth_password,
|
password=self.auth_password,
|
||||||
) as con:
|
) as con:
|
||||||
yield con
|
yield con
|
||||||
|
# unable to connect
|
||||||
except OperationalError as e:
|
except OperationalError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise DatabaseConnectionError()
|
raise DatabaseConnectionError()
|
||||||
|
# invalid credentials / user does not exist / invalid permissions for action
|
||||||
except ProgrammingError as e:
|
except ProgrammingError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise DatabasePermissionError()
|
raise DatabasePermissionError()
|
||||||
|
|
|
@ -39,9 +39,11 @@ class PostgreSQLService:
|
||||||
)
|
)
|
||||||
con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
|
||||||
yield con
|
yield con
|
||||||
|
# unable to connect / invalid credentials / user does not exist
|
||||||
except OperationalError as e:
|
except OperationalError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise DatabaseConnectionError()
|
raise DatabaseConnectionError()
|
||||||
|
# invalid permissions for action
|
||||||
except ProgrammingError as e:
|
except ProgrammingError as e:
|
||||||
logger.error(e)
|
logger.error(e)
|
||||||
raise DatabasePermissionError()
|
raise DatabasePermissionError()
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
import pytest, os
|
||||||
|
|
||||||
|
from click.testing import CliRunner
|
||||||
|
from ceo.cli import cli
|
||||||
|
|
||||||
|
from mysql.connector import connect
|
||||||
|
from mysql.connector.errors import ProgrammingError
|
||||||
|
|
||||||
|
|
||||||
|
def mysql_attempt_connection(host, username, password):
|
||||||
|
with connect(
|
||||||
|
host=host,
|
||||||
|
user=username,
|
||||||
|
password=password,
|
||||||
|
) as con, con.cursor() as cur:
|
||||||
|
cur.execute("SHOW DATABASES")
|
||||||
|
response = cur.fetchall()
|
||||||
|
assert len(response) == 2
|
||||||
|
|
||||||
|
with pytest.raises(ProgrammingError):
|
||||||
|
cur.execute("CREATE DATABASE new_db")
|
||||||
|
|
||||||
|
|
||||||
|
def test_mysql(cli_setup, cfg, ldap_user):
|
||||||
|
runner = CliRunner()
|
||||||
|
|
||||||
|
username = ldap_user.uid
|
||||||
|
host = cfg.get("mysql_host")
|
||||||
|
info_file_path = os.path.join(ldap_user.home_directory, "ceo-mysql-info")
|
||||||
|
assert not os.path.isfile(info_file_path)
|
||||||
|
|
||||||
|
# create database for user
|
||||||
|
result = runner.invoke(cli, ['mysql', 'create', username])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert os.path.isfile(info_file_path)
|
||||||
|
|
||||||
|
response_arr = result.output.split()
|
||||||
|
passwd = response_arr[response_arr.index("Password:") + 1]
|
||||||
|
with open(info_file_path, 'r') as file:
|
||||||
|
old_info = file.read()
|
||||||
|
|
||||||
|
expected = f"""MySQL database created
|
||||||
|
|
||||||
|
Connection Information:
|
||||||
|
|
||||||
|
Database: {username}
|
||||||
|
Username: {username}
|
||||||
|
Password: {passwd}
|
||||||
|
Host: {host}
|
||||||
|
|
||||||
|
Settings and more info has been written to {info_file_path}"""
|
||||||
|
|
||||||
|
assert result.output == expected
|
||||||
|
mysql_attempt_connection(host, username, passwd)
|
||||||
|
|
||||||
|
# perform password reset for user
|
||||||
|
result = runner.invoke(cli, ['mysql', 'pwreset', username], input="y\n")
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
response_arr = result.output.split()
|
||||||
|
new_passwd = response_arr[response_arr.index("Password:") + 1]
|
||||||
|
with open(info_file_path, 'r') as file:
|
||||||
|
new_info = file.read()
|
||||||
|
|
||||||
|
assert new_passwd != passwd
|
||||||
|
assert old_info != new_info
|
||||||
|
mysql_attempt_connection(host, username, new_passwd)
|
||||||
|
|
||||||
|
# delete database and file
|
||||||
|
result = runner.invoke(cli, ['mysql', 'delete', username], input="y\n")
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
# user should be deleted
|
||||||
|
with pytest.raises(ProgrammingError):
|
||||||
|
mysql_attempt_connection(host, username, passwd)
|
||||||
|
|
||||||
|
os.remove(info_file_path)
|
|
@ -0,0 +1,79 @@
|
||||||
|
import pytest, os
|
||||||
|
|
||||||
|
from click.testing import CliRunner
|
||||||
|
from ceo.cli import cli
|
||||||
|
|
||||||
|
from psycopg2 import connect, OperationalError, ProgrammingError
|
||||||
|
|
||||||
|
|
||||||
|
def psql_attempt_connection(host, username, password):
|
||||||
|
con = connect(
|
||||||
|
host=host,
|
||||||
|
user=username,
|
||||||
|
password=password,
|
||||||
|
)
|
||||||
|
con.autocommit = True
|
||||||
|
with con.cursor() as cur:
|
||||||
|
cur.execute("SELECT datname FROM pg_database")
|
||||||
|
response = cur.fetchall()
|
||||||
|
# 3 of the 4 are postgres, template0, template1
|
||||||
|
assert len(response) == 4
|
||||||
|
with pytest.raises(ProgrammingError):
|
||||||
|
cur.execute("CREATE DATABASE new_db")
|
||||||
|
con.close()
|
||||||
|
|
||||||
|
|
||||||
|
def test_postgresql(cli_setup, cfg, ldap_user):
|
||||||
|
runner = CliRunner()
|
||||||
|
|
||||||
|
username = ldap_user.uid
|
||||||
|
host = cfg.get("postgresql_host")
|
||||||
|
info_file_path = os.path.join(ldap_user.home_directory, "ceo-psql-info")
|
||||||
|
assert not os.path.isfile(info_file_path)
|
||||||
|
|
||||||
|
# create database for user
|
||||||
|
result = runner.invoke(cli, ['postgresql', 'create', username])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert os.path.isfile(info_file_path)
|
||||||
|
|
||||||
|
response_arr = result.output.split()
|
||||||
|
passwd = response_arr[response_arr.index("Password:") + 1]
|
||||||
|
with open(info_file_path, 'r') as file:
|
||||||
|
old_info = file.read()
|
||||||
|
|
||||||
|
expected = f"""PostgreSQL database created
|
||||||
|
|
||||||
|
Connection Information:
|
||||||
|
|
||||||
|
Database: {username}
|
||||||
|
Username: {username}
|
||||||
|
Password: {passwd}
|
||||||
|
Host: {host}
|
||||||
|
|
||||||
|
Settings and more info has been written to {info_file_path}"""
|
||||||
|
|
||||||
|
assert result.output == expected
|
||||||
|
psql_attempt_connection(host, username, passwd)
|
||||||
|
|
||||||
|
# perform password reset for user
|
||||||
|
result = runner.invoke(cli, ['postgresql', 'pwreset', username], input="y\n")
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
response_arr = result.output.split()
|
||||||
|
new_passwd = response_arr[response_arr.index("Password:") + 1]
|
||||||
|
with open(info_file_path, 'r') as file:
|
||||||
|
new_info = file.read()
|
||||||
|
|
||||||
|
assert new_passwd != passwd
|
||||||
|
assert old_info != new_info
|
||||||
|
psql_attempt_connection(host, username, new_passwd)
|
||||||
|
|
||||||
|
# delete database and file
|
||||||
|
result = runner.invoke(cli, ['postgresql', 'delete', username], input="y\n")
|
||||||
|
assert result.exit_code == 0
|
||||||
|
|
||||||
|
# user should be deleted
|
||||||
|
with pytest.raises(OperationalError):
|
||||||
|
psql_attempt_connection(host, username, passwd)
|
||||||
|
|
||||||
|
os.remove(info_file_path)
|
Loading…
Reference in New Issue