[cli][tests] Changed the routes names and wrote the tests according to current configuration

This commit is contained in:
Maxime Alves LIRMM@home 2020-08-07 00:00:33 +02:00
parent ad1304f8d4
commit c4872ec0b3
6 changed files with 181 additions and 172 deletions

View File

@ -10,11 +10,6 @@ def cli(ctx, version):
if version: if version:
import halfapi import halfapi
return click.echo(halfapi.version) return click.echo(halfapi.version)
#if not IS_PROJECT:
# return init()
#if ctx.invoked_subcommand is None:
# return run()
if IS_PROJECT: if IS_PROJECT:
import halfapi.cli.domain import halfapi.cli.domain

View File

@ -15,48 +15,15 @@ from halfapi.db import (
logger = logging.getLogger('halfapi') logger = logging.getLogger('halfapi')
#################
# domain create #
#################
def create_domain(name):
pass
def delete_domain(domain): ###############
d = Domain(name=domain) # domain read #
if len(d) != 1: ###############
return False
d.delete(delete_all=True)
return True
@click.option('--domain', '-d', default=None, multiple=True)
@click.option('--update', default=False, is_flag=True)
@cli.command()
def domain(domain, update):
"""
Lists routes for the specified domains, or update them in the database
Parameters:
domain (List[str]|None): The list of the domains to list/update
The parameter has a misleading name as it is a multiple option
but this would be strange to use it several times named as "domains"
update (boolean): If set, update the database for the selected domains
"""
if not domain:
domain = DOMAINS
else:
for domain_name in domain:
if domain_name in DOMAINS:
continue
click.echo(
f'Domain {domain}s is not activated in the configuration')
if update:
update_db(domain)
else:
for domain_name in domain:
list_routes(domain)
def list_routes(domain): def list_routes(domain):
click.echo(f'\nDomain : {domain}') click.echo(f'\nDomain : {domain}')
routers = APIRouter(domain=domain) routers = APIRouter(domain=domain)
@ -69,7 +36,9 @@ def list_routes(domain):
route['acls'] = acls route['acls'] = acls
click.echo('- [{http_verb}] {path} ({acls})'.format(**route)) click.echo('- [{http_verb}] {path} ({acls})'.format(**route))
#################
# domain update #
#################
def update_db(domains): def update_db(domains):
def add_domain(domain): def add_domain(domain):
@ -279,3 +248,63 @@ def update_db(domains):
# @TODO : Insertion exception handling # @TODO : Insertion exception handling
print(e) print(e)
continue continue
#################
# domain delete #
#################
def delete_domain(domain):
d = Domain(name=domain)
if len(d) != 1:
return False
d.delete(delete_all=True)
return True
@click.option('--read',default=False, is_flag=True)
@click.option('--create',default=False, is_flag=True)
@click.option('--update',default=False, is_flag=True)
@click.option('--delete',default=False, is_flag=True)
@click.option('--domains',default=None)
@cli.command()
def domain(domains, delete, update, create, read): #, domains, read, create, update, delete):
"""
Lists routes for the specified domains, or update them in the database
Parameters:
domain (List[str]|None): The list of the domains to list/update
The parameter has a misleading name as it is a multiple option
but this would be strange to use it several times named as "domains"
update (boolean): If set, update the database for the selected domains
"""
if not domains:
if create:
return create_domain()
domains = DOMAINS
else:
domains_ = []
for domain_name in domains.split(','):
if domain_name in DOMAINS:
domains.append(domain_name)
continue
click.echo(
f'Domain {domain_name}s is not activated in the configuration')
domains = domains_
update = False
for domain in domains:
if update:
update_db(domain)
if delete:
delete_domain(domain)
else:
list_routes(domain)

View File

@ -12,9 +12,8 @@ from .cli import cli
logger = logging.getLogger('halfapi') logger = logging.getLogger('halfapi')
TMPL_HALFAPI_ETC = """Insert this into the HALFAPI_CONF_DIR/{project} file TMPL_HALFAPI_ETC = """[project]
name = {project}
[project]
host = 127.0.0.1 host = 127.0.0.1
port = 8000 port = 8000
secret = /path/to/secret_file secret = /path/to/secret_file
@ -22,6 +21,12 @@ production = False
base_dir = {base_dir} base_dir = {base_dir}
""" """
def format_halfapi_etc(project, path):
return TMPL_HALFAPI_ETC.format(
project=project,
base_dir=path
)
TMPL_HALFAPI_CONFIG = """[project] TMPL_HALFAPI_CONFIG = """[project]
name = {name} name = {name}
halfapi_version = {halfapi_version} halfapi_version = {halfapi_version}
@ -59,8 +64,8 @@ def init(project):
halfapi_version=__version__ halfapi_version=__version__
)) ))
print(TMPL_HALFAPI_ETC.format( click.echo(f'Insert this into the HALFAPI_CONF_DIR/{project} file')
project=project, click.echo(format_halfapi_etc(
base_dir=os.path.abspath(project) project,
)) os.path.abspath(project)))

View File

@ -7,6 +7,7 @@ from configparser import ConfigParser
IS_PROJECT = os.path.isfile('.halfapi/config') IS_PROJECT = os.path.isfile('.halfapi/config')
if IS_PROJECT: if IS_PROJECT:
default_config = { default_config = {
'project': { 'project': {
'host': '127.0.0.1', 'host': '127.0.0.1',
@ -35,17 +36,25 @@ if IS_PROJECT:
CONF_DIR = environ.get('HALFAPI_CONF_DIR', '/etc/half_api') CONF_DIR = environ.get('HALFAPI_CONF_DIR', '/etc/half_api')
config.read(filenames=[os.path.join( HALFAPI_CONF_FILE=os.path.join(
CONF_DIR, CONF_DIR,
PROJECT_NAME PROJECT_NAME
)]) )
if not os.path.isfile(HALFAPI_CONF_FILE):
print(f'Missing {HALFAPI_CONF_FILE}, exiting')
sys.exit(1)
config.read(filenames=[HALFAPI_CONF_FILE])
HOST = config.get('project', 'host') HOST = config.get('project', 'host')
PORT = config.getint('project', 'port') PORT = config.getint('project', 'port')
DB_NAME = f'halfapi_{PROJECT_NAME}' DB_NAME = f'halfapi_{PROJECT_NAME}'
with open(config.get('project', 'secret')) as secret_file: try:
SECRET = secret_file.read() with open(config.get('project', 'secret')) as secret_file:
SECRET = secret_file.read()
except FileNotFoundError:
print('There is no file like {}'.format(config.get('project', 'secret')))
sys.exit(1)
PRODUCTION = config.getboolean('project', 'production') PRODUCTION = config.getboolean('project', 'production')
BASE_DIR = config.get('project', 'base_dir') BASE_DIR = config.get('project', 'base_dir')

View File

@ -1,7 +1,57 @@
# content of conftest.py #!/usr/bin/env python3
import re
import os
import subprocess
import importlib
import tempfile
from unittest.mock import patch
from typing import Dict, Tuple from typing import Dict, Tuple
import pytest import pytest
from uuid import uuid1
from click.testing import CliRunner
from halfapi import __version__
from halfapi.cli import cli
from halfapi.cli.init import format_halfapi_etc
Cli = cli.cli
PROJNAME = os.environ.get('PROJ','tmp_api')
@pytest.fixture
def runner():
return CliRunner()
@pytest.fixture
def dropdb():
p = subprocess.Popen(['dropdb', f'halfapi_{PROJNAME}'])
p.wait()
yield
p = subprocess.Popen(['dropdb', f'halfapi_{PROJNAME}'])
p.wait()
@pytest.fixture
def createdb():
p = subprocess.Popen(['createdb', f'halfapi_{PROJNAME}'])
p.wait()
return
def confdir(dirname):
d = os.environ.get(dirname)
if not d:
os.environ[dirname] = tempfile.mkdtemp(prefix='halfapi_')
return os.environ.get(dirname)
if not os.path.isdir(d):
os.mkdir(d)
return d
@pytest.fixture
def halform_conf_dir():
return confdir('HALFORM_CONF_DIR')
@pytest.fixture
def halfapi_conf_dir():
return confdir('HALFAPI_CONF_DIR')
# store history of failures per test class name and per index in parametrize (if # store history of failures per test class name and per index in parametrize (if
# parametrize used) # parametrize used)
@ -51,3 +101,28 @@ def pytest_runtest_setup(item):
# test name # test name
if test_name is not None: if test_name is not None:
pytest.xfail("previous test failed ({})".format(test_name)) pytest.xfail("previous test failed ({})".format(test_name))
@pytest.fixture
def project_runner(runner, dropdb, createdb, halform_conf_dir, halfapi_conf_dir):
env = {
'HALFORM_CONF_DIR': halform_conf_dir,
'HALFAPI_CONF_DIR': halfapi_conf_dir
}
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME],
env=env,
catch_exceptions=True)
assert res.exit_code == 0
os.chdir(PROJNAME)
secret = tempfile.mkstemp()
SECRET_PATH = secret[1]
with open(SECRET_PATH, 'w') as f:
f.write(str(uuid1()))
with open(os.path.join(halfapi_conf_dir, PROJNAME), 'w') as f:
PROJ_CONFIG = re.sub('secret = .*', f'secret = {SECRET_PATH}',
format_halfapi_etc(PROJNAME, os.getcwd()))
f.write(PROJ_CONFIG)
yield lambda args: runner.invoke(Cli, args, env=env)

View File

@ -2,6 +2,8 @@
import os import os
import subprocess import subprocess
import importlib import importlib
import tempfile
from unittest.mock import patch
import pytest import pytest
from click.testing import CliRunner from click.testing import CliRunner
@ -13,26 +15,6 @@ Cli = cli.cli
PROJNAME = os.environ.get('PROJ','tmp_api') PROJNAME = os.environ.get('PROJ','tmp_api')
@pytest.fixture
def runner():
return CliRunner()
@pytest.fixture
def dropdb():
p = subprocess.Popen(['dropdb', f'halfapi_{PROJNAME}'])
p.wait()
yield
p = subprocess.Popen(['dropdb', f'halfapi_{PROJNAME}'])
p.wait()
@pytest.fixture
def createdb():
p = subprocess.Popen(['createdb', f'halfapi_{PROJNAME}'])
p.wait()
return
@pytest.mark.incremental @pytest.mark.incremental
class TestCli(): class TestCli():
@ -79,12 +61,12 @@ class TestCli():
r = runner.invoke(Cli, ['init', testproject]) r = runner.invoke(Cli, ['init', testproject])
assert r.exit_code == 1 assert r.exit_code == 1
def test_init_project(self, runner, dropdb, createdb): def test_init_project(self, runner, dropdb, createdb, halform_conf_dir, halfapi_conf_dir):
cp = ConfigParser() cp = ConfigParser()
with runner.isolated_filesystem(): with runner.isolated_filesystem():
env = { env = {
'HALFORM_CONF_DIR': os.environ.get('HALFORM_CONF_DIR', os.getcwd()), 'HALFORM_CONF_DIR': halform_conf_dir,
'HALFAPI_CONF_DIR': os.environ.get('HALFAPI_CONF_DIR', os.getcwd()), 'HALFAPI_CONF_DIR': halfapi_conf_dir
} }
res = runner.invoke(Cli, ['init', PROJNAME], env=env) res = runner.invoke(Cli, ['init', PROJNAME], env=env)
@ -105,96 +87,10 @@ class TestCli():
assert os.path.isfile(os.path.join(PROJNAME, '.halfapi', 'domains')) assert os.path.isfile(os.path.join(PROJNAME, '.halfapi', 'domains'))
cp.read(os.path.join(PROJNAME, '.halfapi', 'domains')) cp.read(os.path.join(PROJNAME, '.halfapi', 'domains'))
assert cp.has_section('domains') assert cp.has_section('domains')
except AssertionError: except AssertionError as e:
subprocess.run(['tree', '-a', os.getcwd()]) subprocess.run(['tree', '-a', os.getcwd()])
raise e
assert res.exit_code == 0 assert res.exit_code == 0
assert res.exception is None assert res.exception is None
def test_run_commands(self, runner, dropdb, createdb):
def reloadcli():
importlib.reload(cli)
return cli.cli
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
assert res.exit_code == 0
os.chdir(PROJNAME)
Cli2 = reloadcli()
res = runner.invoke(Cli2, ['run', '--help'])
assert res.exception is None
assert res.exit_code == 0
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['run', 'foobar'])
assert res.exit_code == 2
def test_domain_commands(self, runner, dropdb, createdb):
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', 'foobar'])
assert res.exit_code == 2
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', '--help'])
assert r.exit_code == 0
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', 'create', '--help'])
assert r.exit_code == 0
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', 'read', '--help'])
assert r.exit_code == 0
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', 'update', '--help'])
assert r.exit_code == 0
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
os.chdir(PROJNAME)
res = runner.invoke(Cli, ['domain', 'delete', '--help'])
assert r.exit_code == 0
def test_domain_create(self, runner, dropdb):
with runner.isolated_filesystem():
res = runner.invoke(Cli, ['init', PROJNAME])
assert res.exit_code == 0
os.chdir(PROJNAME)
DOMNAME='tmp_domain'
res = runner.invoke(Cli, ['domain', 'create', DOMNAME])
srcdir = os.path.join('domains', 'src', DOMNAME)
assert os.path.isdir(srcdir)
moddir = os.path.join(srcdir, DOMNAME)
assert os.path.isdir(moddir)
setup = os.path.join(srcdir, 'setup.py')
assert os.path.isfile(setup)
initfile = os.path.join(moddir, '__init__.py')
assert os.path.isfile(initfile)
aclfile = os.path.join(moddir, 'acl.py')
assert os.path.isfile(aclfile)
aclsdir = os.path.join(moddir, 'acls')
assert os.path.isdir(aclsdir)
routersdir = os.path.join(moddir, 'routers')
assert os.path.isdir(routersdir)
try:
dom_mod = importlib.import_module(DOMNAME, srcdir)
assert hasattr(dom_mod, 'ROUTERS')
except ImportError:
assert False