Skip to content

HelpersTask629_Add_tests_for_dockerized_executable_sync_gh_issue_labels #649

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
67e33e6
HelpersTask629: Work in progress
sandeepthalapanane Apr 30, 2025
599a565
HelpersTask629: Work in progress
sandeepthalapanane May 1, 2025
6729dfa
HelpersTask629: Work in progress
sandeepthalapanane May 1, 2025
e5e758f
HelpersTask629: Added test for sync_gh_issue_labels
sandeepthalapanane May 2, 2025
d5e7e33
HelpersTask629: Modify docstring
sandeepthalapanane May 2, 2025
a51629e
HelpersTask629: Remove logging which was added for debugging
sandeepthalapanane May 2, 2025
d927547
Merge remote-tracking branch 'origin/master' into HelpersTask629_Add_…
sandeepthalapanane May 2, 2025
4a96eef
HelpersTask629: Modify test yaml file
sandeepthalapanane May 2, 2025
a3de9c5
Merge branch 'master' into HelpersTask629_Add_tests_for_dockerized_ex…
sandeepthalapanane May 2, 2025
605373c
Merge remote-tracking branch 'origin/master' into HelpersTask629_Add_…
sandeepthalapanane May 5, 2025
e7c27e4
HelpersTask629: Modify test to mock GH only
sandeepthalapanane May 5, 2025
219fdc1
HelpersTask629: Modify dockerized test to slow
sandeepthalapanane May 5, 2025
16fc945
HelpersTask629: Skip the test
sandeepthalapanane May 5, 2025
fe9ecc7
Merge master
sandeepthalapanane May 6, 2025
a57d627
HelpersTask629: Remove renamed files
sandeepthalapanane May 6, 2025
89278b2
HelpersTask629: Modify test_sync_gh_issue_labels1 docstring
sandeepthalapanane May 6, 2025
47a234d
HelpersTask629: Add GitHub Mocks
sandeepthalapanane May 7, 2025
0f88027
Merge remote-tracking branch 'origin/master' into HelpersTask629_Add_…
sandeepthalapanane May 7, 2025
aa5dd4b
HelpersTask629: Modify input arguments
sandeepthalapanane May 7, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Test labels for GitHub issue label synchronization.
- name: bug
description: Bug label
color: "#d73a4a"
- name: feature
description: Feature request
color: "#00FF00"
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import logging
import os
import unittest.mock as umock
from typing import Dict, Generator, List, Optional

import pytest

import dev_scripts_helpers.github.dockerized_sync_gh_issue_labels as dshgdsgil
import helpers.hio as hio
import helpers.hunit_test as hunitest

_LOG = logging.getLogger(__name__)


def _get_label_data() -> Dict[str, str]:
return {"name": "test", "description": "test label", "color": "FF0000"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why we don't use the Label object right away so there's no extra layer of conversion?



def _make_mock_label(label_data: Dict[str, str]) -> umock.Mock:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add doc string

label = umock.Mock()
label.name = label_data["name"]
label.color = label_data["color"]
label.description = label_data["description"]
return label


# #############################################################################
# TestLabel1
# #############################################################################


class TestLabel1(hunitest.TestCase):

def test_save_label(self) -> None:
"""
Test saving a label object to a file.
"""
# Prepare inputs.
label_data = _get_label_data()
input_label = dshgdsgil.Label(
label_data["name"], label_data["description"], label_data["color"]
)
temp_dir = self.get_scratch_space()
tmp_file_name = os.path.join(temp_dir, "test_labels.yaml")
# Run.
dshgdsgil.Label.save_labels([input_label], tmp_file_name)
# Check the output.
actual = hio.from_file(tmp_file_name)
expected = f"""
- name: {label_data["name"]}
description: {label_data["description"]}
color: {label_data["color"]}
"""
self.assert_equal(actual, expected, fuzzy_match=True)

def test_load_label(self) -> None:
"""
Test loading a label object from a file.
"""
# Prepare inputs.
label_data = _get_label_data()
temp_dir = self.get_scratch_space()
tmp_file_name = os.path.join(temp_dir, "test_labels.yaml")
# Create a file with known content.
txt = f"""
- name: {label_data["name"]}
description: {label_data["description"]}
color: {label_data["color"]}
"""
hio.to_file(tmp_file_name, txt)
# Load the label.
output_labels = dshgdsgil.Label.load_labels(tmp_file_name)
output_label = output_labels[0]
# Check the output.
actual = f"""
name={output_label.name}
description={output_label.description}
color={output_label.color}
"""
expected = f"""
name={label_data["name"]}
description={label_data["description"]}
color={label_data["color"]}
"""
self.assert_equal(actual, expected, fuzzy_match=True)


# #############################################################################
# TestDockerizedSyncGitHubIssueLabels
# #############################################################################


class TestDockerizedSyncGitHubIssueLabels(hunitest.TestCase):

@pytest.fixture(autouse=True)
def setup_teardown_test(self) -> Generator:
# Run before each test.
self.set_up_test()
yield
# Run after each test.
self.tear_down_test()

def set_up_test(self) -> None:
self.setUp()
# Prepare inputs.
self.temp_dir = self.get_scratch_space()
self.tmp_file_name = os.path.join(self.temp_dir, "test_labels.yaml")
self.input_args = {
"owner": "test-org",
"repo": "test-repo",
"token_env_var": "GITHUB_TOKEN",
"input_file": self.tmp_file_name,
"no_interactive": True,
}
# Mock the GitHub client.
self.mock_repo = umock.Mock()
self.mock_client = umock.Mock()
self.mock_client.get_repo.return_value = self.mock_repo
self.github_patch = umock.patch(
"github.Github", return_value=self.mock_client
)
self.env_patch = umock.patch.dict(
os.environ, {"GITHUB_TOKEN": "fake_token"}
)
self.github_patch.start()
self.env_patch.start()

def tear_down_test(self) -> None:
# Stop the patches.
self.github_patch.stop()
self.env_patch.stop()

def test_create_new_label(self) -> None:
"""
Test that a new label is created when it exists in yaml but not in
repo.
"""
# Prepare inputs.
self.mock_repo.get_labels.return_value = []
label_data = _get_label_data()
input_label = self._save_label(label_data)
# Run.
self._run_with_args()
# Check if the mock was called.
self.mock_repo.create_label.assert_called_once_with(
name=input_label.name,
color=input_label.color,
description=input_label.description,
)

def test_update_existing_label(self) -> None:
"""
Test that a label is updated when it exists in both yaml and repo with
different properties.
"""
# Prepare inputs.
repo_label_data = {
"name": "test",
"description": "old desc",
"color": "00FF00",
}
yaml_label_data = {
"name": "test",
"description": "new desc",
"color": "FF0000",
}
mock_label = _make_mock_label(repo_label_data)
self.mock_repo.get_labels.return_value = [mock_label]
input_label = self._save_label(yaml_label_data)
# Run.
self._run_with_args()
# Check if the mock was called.
mock_label.edit.assert_called_once_with(
name=input_label.name,
color=input_label.color,
description=input_label.description,
)

def test_identical_label(self) -> None:
"""
Test that no changes are made when label exists and is identical.
"""
# Prepare inputs.
label_data = _get_label_data()
mock_label = _make_mock_label(label_data)
self.mock_repo.get_labels.return_value = [mock_label]
self._save_label(label_data)
# Run.
self._run_with_args()
# Check if the mock was called.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check if the create operation was called too?

mock_label.edit.assert_not_called()
mock_label.delete.assert_not_called()

def test_prune_label(self) -> None:
"""
Test that labels that exist in repo but not in yaml are deleted when
prune is enabled.
"""
# Prepare inputs.
repo_label1 = {
"name": "to_prune1",
"description": "desc1",
"color": "FF0000",
}
repo_label2 = {
"name": "to_prune2",
"description": "desc2",
"color": "00FF00",
}
mock_label1 = _make_mock_label(repo_label1)
mock_label2 = _make_mock_label(repo_label2)
self.mock_repo.get_labels.return_value = [mock_label1, mock_label2]
# Test with no labels in the YAML file.
dshgdsgil.Label.save_labels([], self.tmp_file_name)
# Run.
self._run_with_args(extra_args=["--prune"])
# Check if the mock was called.
mock_label1.delete.assert_called_once()
mock_label2.delete.assert_called_once()

def _run_with_args(self, *, extra_args: Optional[List[str]] = None) -> None:
"""
Run `dockerized_sync_gh_issue_labels.py` script with the given
arguments.

:param extra_args: additional arguments to pass to the script
"""
args = [
"--input_file",
self.input_args["input_file"],
"--owner",
self.input_args["owner"],
"--repo",
self.input_args["repo"],
"--token_env_var",
self.input_args["token_env_var"],
]
if self.input_args.get("no_interactive", False):
args.append("--no_interactive")
if extra_args:
args += extra_args
with umock.patch("sys.argv", ["script"] + args):
parser = dshgdsgil._parse()
dshgdsgil._main(parser)

def _save_label(self, label_data: Dict[str, str]) -> dshgdsgil.Label:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use the dshgdsgil.Label.save_labels directly so that we don't have to create another wrapper around it?

"""
Create a Label from label_data and save it to a YAML file.

:param label_data: the label data to save
:return: the Label instance
"""
label = dshgdsgil.Label(
label_data["name"], label_data["description"], label_data["color"]
)
dshgdsgil.Label.save_labels([label], self.tmp_file_name)
return label
81 changes: 81 additions & 0 deletions dev_scripts_helpers/github/test/test_sync_gh_issue_labels.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import logging
import os
import unittest.mock as umock

import pytest

import dev_scripts_helpers.github.sync_gh_issue_labels as dshgsgila
import helpers.hgit as hgit
import helpers.hserver as hserver
import helpers.hunit_test as hunitest

_LOG = logging.getLogger(__name__)


# #############################################################################
# Test_sync_gh_issue_labels1
# #############################################################################


class Test_sync_gh_issue_labels1(hunitest.TestCase):

@pytest.mark.skipif(
hserver.is_inside_ci() or hserver.is_dev_csfy(),
reason="Disabled because of CmampTask10710",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you able to run this test locally?
I'm okay with removing this test as well, since the cases covered here are already handled in the other files. Your call.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just upgraded to Ubuntu 24 and will be testing it out. I believe this test is important to ensure that the Docker container functions without any issues. Since we have already tested the functionalities in the dockerized test script, I want to verify that the Docker container operates smoothly as well. This way, we can ensure that everything runs smoothly and ticks all the boxes.

)
@umock.patch(
"dev_scripts_helpers.github.dockerized_sync_gh_issue_labels.github.Github"
)
def test1(self, mock_github: umock.Mock) -> None:
"""
Test that the dockerized executable reads the input file, performs the
necessary operations, and writes the backup file in the git root
directory.
"""
# Set up mock labels.
label_data = {
"name": "bug",
"color": "f29513",
"description": "Something isn't working",
}
mock_label = umock.Mock()
for k, v in label_data.items():
setattr(mock_label, k, v)
Comment on lines +36 to +43
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about creating a label object and mock it?

# Set up mock GitHub repo.
mock_repo = umock.Mock()
mock_repo.get_labels.return_value = [mock_label]
mock_client = umock.Mock()
mock_client.get_repo.return_value = mock_repo
mock_github.return_value = mock_client
# Prepare input arguments.
input_args = {
"in_dir_name": self.get_input_dir(),
"owner": "test-org",
"repo": "test-repo",
"token_env_var": "GITHUB_TEST_TOKEN",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the client running the tests might not have this var set

}
input_file_path = os.path.join(
input_args["in_dir_name"], "test_gh_issues_labels.yml"
)
git_root_dir = hgit.get_client_root(False)
backup_file_name = "tmp.labels.test-org.test-repo.yaml"
backup_file_path = os.path.join(git_root_dir, backup_file_name)
# Remove backup file if it exists.
if os.path.exists(backup_file_path):
os.remove(backup_file_path)
# Run test.
dshgsgila._run_dockerized_sync_gh_issue_labels(
input_file_path,
input_args["owner"],
input_args["repo"],
input_args["token_env_var"],
dry_run=False,
no_interactive=True,
prune=False,
backup=True,
)
# Check that the backup file exists.
self.assertTrue(
os.path.exists(backup_file_path),
msg="Backup file was not created.",
)
Loading