Skip to content

Commit

Permalink
test: restructure and complete the test suite
Browse files Browse the repository at this point in the history
  • Loading branch information
qdelamea-aneo committed Nov 15, 2024
1 parent 92b6720 commit e18ff73
Show file tree
Hide file tree
Showing 12 changed files with 383 additions and 69 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -75,17 +75,17 @@ jobs:
- name: Get Report
uses: orgoro/coverage@3f13a558c5af7376496aa4848bf0224aead366ac
with:
coverageFile: packages/python/coverage.xml
coverageFile: coverage.xml
token: ${{ secrets.GITHUB_TOKEN }}

- name: Archive code coverage results html
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
with:
name: code-coverage-report-html
path: packages/python/coverage_report
path: coverage_report

- name: Archive code coverage results xml
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a
with:
name: code-coverage-report-xml
path: packages/python/coverage.xml
path: coverage.xml
3 changes: 1 addition & 2 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Python Package

on:
push:
pull_request:
branches:
- main
Expand Down Expand Up @@ -46,7 +45,7 @@ jobs:
packages-dir: dist/
repository-url: https://test.pypi.org/legacy/

- name: Publish to PyPiTest
- name: Publish to PyPi
if: github.event_name == 'release' # Publish on releases
uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
7 changes: 5 additions & 2 deletions src/armonik_cli/commands/sessions.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ def list(endpoint: str, output: str, debug: bool) -> None:
sessions = [_clean_up_status(s) for s in sessions]
console.formatted_print(sessions, format=output, table_cols=SESSION_TABLE_COLS)

console.print(f"\n{total} sessions found.")
# TODO: Use logger to display this information
# console.print(f"\n{total} sessions found.")


@sessions.command()
Expand Down Expand Up @@ -255,7 +256,9 @@ def stop_submission(
session = sessions_client.stop_submission_session(
session_id=session_id, client=clients_only, worker=workers_only
)
console.formatted_print(session, format=output, table_cols=SESSION_TABLE_COLS)
console.formatted_print(
_clean_up_status(session), format=output, table_cols=SESSION_TABLE_COLS
)


def _clean_up_status(session: Session) -> Session:
Expand Down
6 changes: 3 additions & 3 deletions src/armonik_cli/core/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

def error_handler(func=None):
"""Decorator to ensure correct display of errors.
Args:
func: The command function to be decorated. If None, a partial function is returned,
allowing the decorator to be used with parentheses.
Expand Down Expand Up @@ -46,7 +46,7 @@ def wrapper(*args, **kwargs):

def base_command(func=None):
"""Decorator to add common CLI options to a Click command function, including
'endpoint', 'output', and 'debug'. These options are automatically passed
'endpoint', 'output', and 'debug'. These options are automatically passed
as arguments to the decorated function.
The following options are added to the command:
Expand All @@ -55,7 +55,7 @@ def base_command(func=None):
- `--debug`: Enables debug mode, printing additional logs if set.
Warning:
If the decorated function has parameters with the same names as the options added by
If the decorated function has parameters with the same names as the options added by
this decorator, this can lead to conflicts and unpredictable behavior.
Args:
Expand Down
219 changes: 203 additions & 16 deletions tests/commands/test_sessions.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,213 @@
import pytest

from datetime import datetime, timedelta
from copy import deepcopy

from armonik.client import ArmoniKSessions
from armonik.common import Session
from click.testing import CliRunner
from armonik.common import Session, TaskOptions, SessionStatus
from conftest import run_cmd_and_assert_exit_code, reformat_cmd_output

ENDPOINT = "172.17.119.85:5001"

raw_session = Session(
session_id="id",
status=SessionStatus.RUNNING,
client_submission=True,
worker_submission=True,
partition_ids=["default"],
options=TaskOptions(
max_duration=timedelta(hours=1),
priority=1,
max_retries=2,
partition_id="default",
application_name="",
application_version="",
application_namespace="",
application_service="",
engine_type="",
options={},
),
created_at=datetime(year=2024, month=11, day=11),
cancelled_at=None,
closed_at=None,
purged_at=None,
deleted_at=None,
duration=timedelta(hours=0),
)
serialized_session = {
"SessionId": "id",
"Status": "Running",
"ClientSubmission": True,
"WorkerSubmission": True,
"PartitionIds": ["default"],
"Options": {
"MaxDuration": "1:00:00",
"Priority": 1,
"MaxRetries": 2,
"PartitionId": "default",
"ApplicationName": "",
"ApplicationVersion": "",
"ApplicationNamespace": "",
"ApplicationService": "",
"EngineType": "",
"Options": {},
},
"CreatedAt": "2024-11-11 00:00:00",
"CancelledAt": None,
"ClosedAt": None,
"PurgedAt": None,
"DeletedAt": None,
"Duration": "0:00:00",
}


@pytest.mark.parametrize(
"cmd",
[
f"session list --endpoint {ENDPOINT}",
],
)
def test_session_list(mocker, cmd):
mocker.patch.object(ArmoniKSessions, "list_sessions", return_value=(1, [deepcopy(raw_session)]))
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == [serialized_session]


@pytest.mark.parametrize(
"cmd",
[
f"session get --endpoint {ENDPOINT} id",
],
)
def test_session_get(mocker, cmd):
mocker.patch.object(ArmoniKSessions, "get_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == serialized_session


@pytest.mark.parametrize(
"cmd",
[
f"session create --priority 1 --max-duration 01:00:0 --max-retries 2 --endpoint {ENDPOINT}",
f"session create --priority 1 --max-duration 01:00:0 --max-retries 2 --endpoint {ENDPOINT} "
"--default-partition bench --partition bench --partition htcmock --option op1=val1 --option opt2=val2 "
"--application-name app --application-version v1 --application-namespace ns --application-service svc --engine-type eng",
],
)
def test_session_create(mocker, cmd):
mocker.patch.object(ArmoniKSessions, "create_session", return_value="id")
mocker.patch.object(ArmoniKSessions, "get_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == serialized_session


@pytest.mark.parametrize(
("cmd", "prompt"),
[
(f"session cancel --confirm --endpoint {ENDPOINT} id", None),
(f"session cancel --endpoint {ENDPOINT} id", "y"),
],
)
def test_session_cancel(mocker, cmd, prompt):
mocker.patch.object(ArmoniKSessions, "cancel_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd, input=prompt)
assert (
reformat_cmd_output(
result.output, deserialize=True, first_line_out=True if prompt else False
)
== serialized_session
)


from armonik_cli.commands.sessions import list
@pytest.mark.parametrize(
"cmd",
[
f"session pause --endpoint {ENDPOINT} id",
],
)
def test_session_pause(mocker, cmd):
mocker.patch.object(ArmoniKSessions, "pause_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == serialized_session


@pytest.mark.parametrize(
"cmd",
[
f"session resume --endpoint {ENDPOINT} id",
],
)
def test_session_resume(mocker, cmd):
mocker.patch.object(ArmoniKSessions, "resume_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == serialized_session


@pytest.mark.parametrize(
("cmd", "prompt"),
[
(f"session close --confirm --endpoint {ENDPOINT} id", None),
(f"session close --endpoint {ENDPOINT} id", "y"),
],
)
def test_session_close(mocker, cmd, prompt):
mocker.patch.object(ArmoniKSessions, "close_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd, input=prompt)
assert (
reformat_cmd_output(
result.output, deserialize=True, first_line_out=True if prompt else False
)
== serialized_session
)


@pytest.mark.parametrize(
("cmd", "prompt"),
[
(f"session purge --confirm --endpoint {ENDPOINT} id", None),
(f"session purge --endpoint {ENDPOINT} id", "y"),
],
)
def test_session_purge(mocker, cmd, prompt):
mocker.patch.object(ArmoniKSessions, "purge_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd, input=prompt)
assert (
reformat_cmd_output(
result.output, deserialize=True, first_line_out=True if prompt else False
)
== serialized_session
)


@pytest.mark.parametrize(
("cmd", "prompt"),
[
(f"session delete --confirm --endpoint {ENDPOINT} id", None),
(f"session delete --endpoint {ENDPOINT} id", "y"),
],
)
def test_session_delete(mocker, cmd, prompt):
mocker.patch.object(ArmoniKSessions, "delete_session", return_value=deepcopy(raw_session))
result = run_cmd_and_assert_exit_code(cmd, input=prompt)
assert (
reformat_cmd_output(
result.output, deserialize=True, first_line_out=True if prompt else False
)
== serialized_session
)


@pytest.mark.parametrize(
("args", "mock_return", "output_id"),
"cmd",
[
(["--endpoint", "endpoint"], (0, []), "sessions_list_empty"),
(
["--endpoint", "endpoint", "-o", "json"],
(1, [Session(session_id="id")]),
"sessions_list",
),
f"session stop-submission --endpoint {ENDPOINT} id",
f"session stop-submission --clients-only --endpoint {ENDPOINT} id",
f"session stop-submission --workers-only --endpoint {ENDPOINT} id",
],
)
def test_armonik_sessions_list(mocker, cmd_outputs, args, mock_return, output_id):
mocker.patch.object(ArmoniKSessions, "list_sessions", return_value=mock_return)
runner = CliRunner()
result = runner.invoke(list, args)
assert result.exit_code == 0
assert result.output == cmd_outputs[output_id]
def test_session_stop_submission(mocker, cmd):
mocker.patch.object(
ArmoniKSessions, "stop_submission_session", return_value=deepcopy(raw_session)
)
result = run_cmd_and_assert_exit_code(cmd)
assert reformat_cmd_output(result.output, deserialize=True) == serialized_session
39 changes: 27 additions & 12 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
import pytest
import json

from pathlib import Path
from typing import Dict, Optional

from click.testing import CliRunner, Result

@pytest.fixture
def cmd_outputs():
"""Read command output files located in tests/outputs and return a dictionnary which keys are
file names and values file contents."""
output_files = [
d
for d in (Path(__file__).parent / "outputs").iterdir()
if d.is_file() and d.suffix == ".txt"
]
return {f.name.removesuffix(".txt"): f.open("r").read() for f in output_files}
from armonik_cli.cli import cli


def run_cmd_and_assert_exit_code(
cmd: str, exit_code: int = 0, input: Optional[str] = None, env: Optional[Dict[str, str]] = None
) -> Result:
cmd = cmd.split()
runner = CliRunner()
with runner.isolated_filesystem():
result = runner.invoke(cli, cmd, input=input, env=env)
assert result.exit_code == exit_code
return result


def reformat_cmd_output(
output: str, deserialize: bool = False, first_line_out: bool = False
) -> str:
if first_line_out:
output = "\n".join(output.split("\n")[1:])
output = output.replace("\n", "")
output = " ".join(output.split())
if deserialize:
return json.loads(output)
return output
Loading

0 comments on commit e18ff73

Please sign in to comment.