Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/polarion/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class PolarionTestCaseApprovalError(Exception):
pass


class PolarionTestCaseWithoutRequirementError(Exception):
pass
71 changes: 71 additions & 0 deletions apps/polarion/polarion_set_automated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations
import logging
import os
from simple_logger.logger import get_logger

import click

from apps.polarion.exceptions import PolarionTestCaseApprovalError
from apps.polarion.polarion_utils import get_polarion_project_id, find_polarion_ids, update_polarion_ids
from typing import List, Dict, Optional


LOGGER = get_logger(name=__name__)


def approve_tests(polarion_project_id: str, added_ids: List[str]) -> Dict[str, List[str]]:
LOGGER.debug(f"Following polarion ids were added: {added_ids}")
return update_polarion_ids(
polarion_ids=list(added_ids), project_id=polarion_project_id, is_automated=True, is_approved=True
)


def remove_approved_tests(polarion_project_id: str, added_ids: Optional[List[str]] = None) -> Dict[str, List[str]]:
removed_polarions = {}
added_ids = added_ids or []
if removed_ids := set(find_polarion_ids(polarion_project_id=polarion_project_id, string_to_match="removed")) - set(
added_ids
):
LOGGER.info(f"Following polarion ids were removed: {removed_ids}")
removed_polarions = update_polarion_ids(
polarion_ids=list(removed_ids), project_id=polarion_project_id, is_automated=False
)
LOGGER.error(f"Following polarion ids marked not automated: {removed_polarions.get('updated')}")
return removed_polarions


@click.command()
@click.option(
"--config-file-path",
help="Provide absolute path to the config file. Any CLI option(s) would override YAML file",
type=click.Path(),
default=os.path.expanduser("~/.config/python-utility-scripts/config.yaml"),
)
@click.option("--project-id", "-p", help="Provide the polarion project id")
@click.option("--verbose", default=False, is_flag=True)
def polarion_approve_automate(config_file_path: str, project_id: str, verbose: bool) -> None:
if verbose:
LOGGER.setLevel(logging.INFO)
else:
logging.disable(logging.ERROR)
polarion_project_id = project_id or get_polarion_project_id(
config_file_path=config_file_path, util_name="pyutils-polarion-set-automated"
)
added_polarions = {}
if added_ids := find_polarion_ids(polarion_project_id=polarion_project_id, string_to_match="added"):
added_polarions = approve_tests(polarion_project_id=polarion_project_id, added_ids=added_ids)
LOGGER.info(f"Following polarion ids were marked automated and approved: {added_polarions.get('updated')}")

removed_polarions = remove_approved_tests(polarion_project_id=polarion_project_id, added_ids=added_ids)
if removed_polarions.get("failed") or added_polarions.get("failed"):
error = "Following polarion ids updates failed."
if removed_polarions.get("failed"):
error += f" Removed ids: {removed_polarions.get('failed')}."
if added_polarions.get("failed"):
error += f" Added ids:: {added_polarions.get('failed')}."
LOGGER.error(error)
raise PolarionTestCaseApprovalError(error)


if __name__ == "__main__":
polarion_approve_automate()
91 changes: 72 additions & 19 deletions apps/polarion/polarion_utils.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,18 @@
from __future__ import annotations
import re

import click
from simple_logger.logger import get_logger
import shlex
import subprocess
from pylero.exceptions import PyleroLibException

from apps.utils import get_util_config
from typing import Dict, List

LOGGER = get_logger(name=__name__)
AUTOMATED = "automated"
NOT_AUTOMATED = "notautomated"
APPROVED = "approved"


def git_diff() -> str:
Expand All @@ -15,33 +23,78 @@ def git_diff() -> str:
def git_diff_lines() -> Dict[str, List[str]]:
diff: Dict[str, List[str]] = {}
for line in git_diff().splitlines():
LOGGER.debug(line)
LOGGER.info(line)
if line.startswith("+"):
diff.setdefault("added", []).append(line)
if line.startswith("-"):
diff.setdefault("removed", []).append(line)
return diff


def validate_polarion_requirements(
polarion_test_ids: List[str],
polarion_project_id: str,
) -> List[str]:
from pylero.work_item import TestCase, Requirement

tests_with_missing_requirements: List[str] = []
if polarion_test_ids:
from pylero.work_item import TestCase, Requirement
from pylero.exceptions import PyleroLibException

for _id in polarion_test_ids:
has_req = False
LOGGER.debug(f"Checking if {_id} verifies any requirement")
tc = TestCase(project_id=polarion_project_id, work_item_id=_id)
for link in tc.linked_work_items:
try:
Requirement(project_id=polarion_project_id, work_item_id=link.work_item_id)
has_req = True
break
except PyleroLibException:
continue

if not has_req:
LOGGER.error(f"{_id}: Is missing requirement")
tests_with_missing_requirements.append(_id)
for _id in polarion_test_ids:
has_req = False
LOGGER.info(f"Checking if {_id} verifies any requirement")
tc = TestCase(project_id=polarion_project_id, work_item_id=_id)
for link in tc.linked_work_items:
try:
Requirement(project_id=polarion_project_id, work_item_id=link.work_item_id)
has_req = True
break
except PyleroLibException:
continue

if not has_req:
LOGGER.error(f"{_id}: Is missing requirement")
tests_with_missing_requirements.append(_id)
return tests_with_missing_requirements


def find_polarion_ids(polarion_project_id: str, string_to_match: str) -> List[str]:
return re.findall(
rf"pytest.mark.polarion.*({polarion_project_id}-[0-9]+)",
"\n".join(git_diff_lines().get(string_to_match, [])),
re.MULTILINE | re.IGNORECASE,
)


def get_polarion_project_id(util_name: str, config_file_path: str) -> str:
polarion_project_id = get_util_config(util_name=util_name, config_file_path=config_file_path).get("project_id")
if not polarion_project_id:
LOGGER.error("Polarion project id must be passed via config file or command line")
raise click.Abort()
return polarion_project_id


def update_polarion_ids(
project_id: str, is_automated: bool, polarion_ids: List[str], is_approved: bool = False
) -> Dict[str, List[str]]:
updated_ids: Dict[str, List[str]] = {}
if polarion_ids:
automation_status = AUTOMATED if is_automated else NOT_AUTOMATED

from pylero.work_item import TestCase
from pylero.exceptions import PyleroLibException

for id in polarion_ids:
try:
tc = TestCase(project_id=project_id, work_item_id=id)
tc.caseautomation = automation_status
if is_approved:
tc.status = APPROVED
tc.update()
LOGGER.info(f"Polarion {id}: marked as: {automation_status}, approved status set: {is_approved}")
updated_ids.setdefault("updated", []).append(id)
except PyleroLibException as polarion_exception:
error = f"{id}: {polarion_exception}"
LOGGER.error(error)
updated_ids.setdefault("failed", []).append(error)
return updated_ids
45 changes: 17 additions & 28 deletions apps/polarion/polarion_verify_tc_requirements.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import re
import logging
from simple_logger.logger import get_logger
import os
import click
from apps.polarion.polarion_utils import (
git_diff_lines,
validate_polarion_requirements,
)
from apps.utils import get_util_config

from apps.polarion.exceptions import PolarionTestCaseWithoutRequirementError
from apps.polarion.polarion_utils import validate_polarion_requirements, find_polarion_ids, get_polarion_project_id

LOGGER = get_logger(name="polarion-verify-tc-requirements")

Expand All @@ -20,32 +17,24 @@
default=os.path.expanduser("~/.config/python-utility-scripts/config.yaml"),
)
@click.option("--project-id", "-p", help="Provide the polarion project id")
@click.option("--verbosity", default=False, is_flag=True)
def has_verify(config_file_path: str, project_id: str, verbosity: bool) -> None:
if verbosity:
LOGGER.setLevel(logging.DEBUG)

polarion_project_id = project_id or get_util_config(
util_name="pyutils-polarion-verify-tc-requirements",
config_file_path=config_file_path,
).get("project_id")

if not polarion_project_id:
LOGGER.error("Polarion project id must be passed via config file or command line")
raise click.Abort()

if added_ids := re.findall(
rf"pytest.mark.polarion.*({polarion_project_id}-[0-9]+)",
"\n".join(git_diff_lines().get("added", [])),
re.MULTILINE | re.IGNORECASE,
):
LOGGER.debug(f"Checking following ids: {added_ids}")
@click.option("--verbose", default=False, is_flag=True)
def has_verify(config_file_path: str, project_id: str, verbose: bool) -> None:
if verbose:
LOGGER.setLevel(logging.INFO)
else:
logging.disable(logging.ERROR)
polarion_project_id = project_id or get_polarion_project_id(
config_file_path=config_file_path, util_name="pyutils-polarion-verify-tc-requirements"
)
if added_ids := find_polarion_ids(polarion_project_id=polarion_project_id, string_to_match="added"):
LOGGER.info(f"Checking following ids: {added_ids}")
if tests_with_missing_requirements := validate_polarion_requirements(
polarion_test_ids=added_ids,
polarion_project_id=polarion_project_id,
):
click.echo(f"TestCases with missing requirement: {tests_with_missing_requirements}")
raise click.Abort()
raise PolarionTestCaseWithoutRequirementError(
f"TestCases with missing requirement: {tests_with_missing_requirements}"
)


if __name__ == "__main__":
Expand Down
4 changes: 3 additions & 1 deletion config.example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ pyutils-unusedcode:
exclude_function_prefix:
- "my_exclude_function_prefix"
pyutils-polarion-verify-tc-requirements:
project_id: "ABC"
project_id: "ABCDEF"
pyutils-polarion-set-automated:
project_id: "ABCDEF"
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
omit = ["tests/*"]

[tool.coverage.report]
fail_under = 75
fail_under = 60
skip_empty = true

[tool.coverage.html]
Expand Down Expand Up @@ -47,6 +47,7 @@ packages = [{ include = "apps" }]
[tool.poetry.scripts]
pyutils-unusedcode = "apps.unused_code.unused_code:get_unused_functions"
pyutils-polarion-verify-tc-requirements = "apps.polarion.polarion_verify_tc_requirements:has_verify"
pyutils-polarion-set-automated = "apps.polarion.polarion_set_automated:polarion_approve_automate"

[tool.poetry.dependencies]
python = "^3.8"
Expand All @@ -65,6 +66,7 @@ ipython = "*"
enable = true
pattern = "((?P<epoch>\\d+)!)?(?P<base>\\d+(\\.\\d+)*)"


[tool.poetry.group.test.dependencies]
pytest = "^8.0.0"
pytest-cov = "^5.0.0"
Expand Down
17 changes: 17 additions & 0 deletions tests/polarion/test_polarion_automated.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import shlex
import subprocess
from pyhelper_utils.shell import run_command

BASE_COMMAND = "poetry run python apps/polarion/polarion_set_automated.py --verbose"


def test_missing_project_id_set_automated():
rc, _, err = run_command(
command=shlex.split(BASE_COMMAND),
verify_stderr=False,
check=False,
capture_output=False,
stderr=subprocess.PIPE,
)
assert "Polarion project id must be passed via config file or command line" in err
assert not rc
2 changes: 1 addition & 1 deletion tests/polarion/test_verify_polarion_requirements.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

def test_missing_project_id():
rc, _, err = run_command(
command=shlex.split(BASE_COMMAND),
command=shlex.split(f"{BASE_COMMAND} --verbose"),
verify_stderr=False,
check=False,
capture_output=False,
Expand Down