From 4d02cf4acd8aa071842eedab7b15e23ad444d176 Mon Sep 17 00:00:00 2001 From: "Maxime Alves LIRMM@home" Date: Wed, 29 Jul 2020 21:03:00 +0200 Subject: [PATCH] [cli] Added init-project command and cli tests --- __init__.py | 0 halfapi/__init__.py | 7 ++- halfapi/cli.py | 88 +++++++++++++++++++++++++++------- pytest.ini | 2 - tests/test_cli.py | 46 ++++++++++++++++++ tests/test_cli_init_project.py | 51 ++++++++++++++++++++ 6 files changed, 173 insertions(+), 21 deletions(-) delete mode 100644 __init__.py create mode 100644 tests/test_cli.py create mode 100644 tests/test_cli_init_project.py diff --git a/__init__.py b/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/halfapi/__init__.py b/halfapi/__init__.py index 02daba3..13cbbb7 100644 --- a/halfapi/__init__.py +++ b/halfapi/__init__.py @@ -1,3 +1,8 @@ #!/usr/bin/env python3 __version__ = '0.1.0' -print(f'HalfAPI version:{__version__}') + +def version(): + return f'HalfAPI version:{__version__}' + +if __name__ == '__main__': + print(version()) diff --git a/halfapi/cli.py b/halfapi/cli.py index f02c778..62c137a 100755 --- a/halfapi/cli.py +++ b/halfapi/cli.py @@ -1,19 +1,10 @@ #!/usr/bin/env python3 -from halfapi.conf import (PROJECT_NAME, HOST, PORT, - PRODUCTION, BASE_DIR, DOMAINS) - -from halfapi.db import ( - Domain, - APIRouter, - APIRoute, - AclFunction, - Acl) - # builtins import click import uvicorn import os import sys +import re import importlib from pprint import pprint @@ -22,16 +13,31 @@ CONTEXT_SETTINGS={ } @click.group(invoke_without_command=True, context_settings=CONTEXT_SETTINGS) +@click.option('--version', is_flag=True) @click.pass_context -def cli(ctx): +def cli(ctx, version): + if version: + import halfapi + return click.echo(halfapi.version()) + if ctx.invoked_subcommand is None: return run() - -@click.option('--host', default=HOST) -@click.option('--port', default=PORT) +@click.option('--host', default=None) +@click.option('--port', default=None) @cli.command() def run(host, port): + from halfapi.conf import (PROJECT_NAME, HOST, PORT, + PRODUCTION, BASE_DIR) + + if not host: + host = HOST + + if not port: + port = PORT + + port = int(port) + debug = reload = not PRODUCTION log_level = 'info' if PRODUCTION else 'debug' @@ -58,18 +64,17 @@ def delete_domain(domain): @click.option('--domain', default=None) @click.option('--update', default=False, is_flag=True) -@click.option('--list', default=False, is_flag=True) @cli.command() -def routes(domain, list, update): +def routes(domain, update): domains = DOMAINS if domain is None else [domain] if update: if not domain: click.echo('No domain name given, will update all active domains') for domain in domains: update_db(domain) - if list: + else: list_routes(domains) - + def list_routes(domains): for domain in domains: @@ -79,6 +84,27 @@ def list_routes(domains): print('-', route) def update_db(domain=None): + from halfapi.db import ( + Domain, + APIRouter, + APIRoute, + AclFunction, + Acl) + + global Domain, APIRouter, APIRoute, AclFunction, Acl + + if domain is None: + from halfapi.conf import DOMAINS + + click.echo('No domain name given, will update all active domains') + for domain in DOMAINS: + dbupdate_fct(domain) + sys.exit(0) + + return dbupdate_fct(domain) + + +def dbupdate_fct(domain=None): if domain is None: click.echo('Missing domain', err=True) sys.exit(1) @@ -195,6 +221,32 @@ def update_db(domain=None): except Exception as e: click.echo(e, err=True) +@click.argument('project') +@click.option('--repo', default=None) +@cli.command() +def init_project(project, repo): + import pygit2 + + if not re.match('^[a-z0-9_]+$', project, re.I): + click.echo('Project name must match "^[a-z0-9_]+$", retry.', err=True) + sys.exit(1) + + if os.path.exists(project): + click.echo(f'A file named {project} already exists, abort.', err=True) + sys.exit(1) + + if repo is not None: + click.echo(f'Clone URL {repo} in directory {project}') + pygit2.clone_repository( + url=repo, + path=project + ) + + else: + click.echo(f'Initialize project repository in directory {project}') + pygit2.init_repository(project) + + if __name__ == '__main__': cli() diff --git a/pytest.ini b/pytest.ini index d70ffb1..25c7847 100644 --- a/pytest.ini +++ b/pytest.ini @@ -2,5 +2,3 @@ testpaths = tests halfapi addopts = --doctest-modules doctest_optionflags = ELLIPSIS -env = - DEBUG=TRUE diff --git a/tests/test_cli.py b/tests/test_cli.py new file mode 100644 index 0000000..f0e4b33 --- /dev/null +++ b/tests/test_cli.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python3 +import os + +import pytest +from click.testing import CliRunner + +from halfapi.cli import cli + +@pytest.fixture +def runner(): + return CliRunner() + +def test_cli(runner): + # Wrong command + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['foobar']) + assert r.exit_code == 2 + + # Test existing commands + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['--help']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['--version']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['dbupdate', '--help']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['init-project', '--help']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['run', '--help']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['domain', '--help']) + assert r.exit_code == 0 + + with runner.isolated_filesystem(): + r = runner.invoke(cli, ['routes', '--help']) + assert r.exit_code == 0 diff --git a/tests/test_cli_init_project.py b/tests/test_cli_init_project.py new file mode 100644 index 0000000..c42140c --- /dev/null +++ b/tests/test_cli_init_project.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +import os + +import pytest +from click.testing import CliRunner + +from halfapi.cli import cli + +@pytest.fixture +def runner(): + return CliRunner() + +def test_init_project(runner): + # Missing argument (project) + r = runner.invoke(cli, ['init-project']) + assert r.exit_code == 2 + + with runner.isolated_filesystem(): + # Fail : Wrong project name + r = runner.invoke(cli, ['init-project', 'test*-project']) + assert r.exit_code == 1 + + with runner.isolated_filesystem(): + # Fail : Already existing folder + os.mkdir('testproject') + r = runner.invoke(cli, ['init-project', 'testproject']) + assert r.exit_code == 1 + + with runner.isolated_filesystem(): + # Fail : Already existing nod + os.mknod('testproject') + r = runner.invoke(cli, ['init-project', 'testproject']) + assert r.exit_code == 1 + + with runner.isolated_filesystem(): + # Success : New repo + r = runner.invoke(cli, ['init-project', 'testproject']) + assert r.exit_code == 0 + assert os.path.isdir('testproject') + assert os.path.isdir('testproject/.git') + + with runner.isolated_filesystem(): + # Success : Cloned repo + import pygit2 + pygit2.init_repository('testproject.git', bare=True) + assert os.path.isdir('testproject.git') + + r = runner.invoke(cli, ['init-project', 'testproject', '--repo', './testproject.git']) + assert r.exit_code == 0 + assert os.path.isdir('testproject') + assert os.path.isdir('testproject/.git')