import pytest import requests import time import json import base64 @pytest.mark.t001 @pytest.mark.xdist_group(name="branch_main") def test_issue_processor_remove_package_via_issue(main_env, test_user_client, unique_id): """ Test scenario TC-RM-001: Package removal via [RM] issue. """ gitea_env, test_full_repo_name, merge_branch_name = main_env pkg_name = f"pkgRM1_{unique_id}" # Pre-create the org label for package removal so the bot can apply it gitea_env.create_org_label("myproducts", "request/Remove Package", "#ff0000") # 1. Create a new repository in mypool's organization gitea_env.setup_repo("mypool", pkg_name) # Add workflow-pr as admin collaborator to the repository so it can rename branches gitea_env._request("PUT", f"repos/mypool/{pkg_name}/collaborators/workflow-pr", json={ "permission": "admin" }) # 4. Get latest commit SHA for pkgRM1 resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches/main") pkg_sha = resp.json()["commit"]["id"] # 5. Read current .gitmodules of myproducts/mySLFO try: resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/contents/.gitmodules") old_gitmodules = base64.b64decode(resp.json()["content"]).decode("utf-8") except Exception: old_gitmodules = "" # 6. Update .gitmodules with our submodule new_gitmodules = old_gitmodules + f"\n[submodule \"{pkg_name}\"]\n path = {pkg_name}\n url = ../../mypool/{pkg_name}.git\n" gitea_env.create_file("myproducts", "mySLFO", ".gitmodules", new_gitmodules, branch="main", message=f"Add submodule {pkg_name}") time.sleep(2) # 7. Add gitlink via diffpatch diff_content = f"""diff --git a/{pkg_name} b/{pkg_name} new file mode 160000 index 00000000..{pkg_sha} --- /dev/null +++ b/{pkg_name} @@ -0,0 +1 @@ +Subproject commit {pkg_sha} """ data = { "branch": "main", "content": diff_content, "message": f"Add {pkg_name} gitlink" } gitea_env._request("POST", "repos/myproducts/mySLFO/diffpatch", json=data) time.sleep(2) # 8. Create [RM] issue in myproducts/mySLFO issue_data = { "title": f"[RM] Remove {pkg_name} from main", "body": f"{pkg_name}", "ref": "refs/heads/main" } response, _ = test_user_client._request("POST", "repos/myproducts/mySLFO/issues", json=issue_data) issue = response.json() issue_number = issue["number"] print(f"Created [RM] issue #{issue_number}") time.sleep(2) # 9. Wait for project PR for package removal to be created prj_pr_number = None for _ in range(60): resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/pulls", params={"state": "open"}) pulls = resp.json() for p in pulls: if p["title"] == f"[RM] Remove packages: {pkg_name}": prj_pr_number = p["number"] break if prj_pr_number: break time.sleep(2) assert prj_pr_number is not None, f"Project PR for package removal of {pkg_name} was not created" # 10. Check if branch in mypool/{pkg_name} was renamed to main-removed renamed = False for _ in range(60): resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches") branches = [b["name"] for b in resp.json()] if "main-removed" in branches and "main" not in branches: renamed = True break time.sleep(2) assert renamed, f"Package branch for {pkg_name} was not renamed to main-removed" # 11. Check if Remove Package label was applied to the issue (with polling for asynchronous application) label_applied = False labels = [] for _ in range(60): resp, _ = gitea_env._request("GET", f"repos/myproducts/mySLFO/issues/{issue_number}") labels = [l["name"] for l in resp.json()["labels"]] if any("Remove Package" in label for label in labels): label_applied = True break time.sleep(2) assert label_applied, f"Expected Remove Package label on issue #{issue_number}, found: {labels}" @pytest.mark.t002 @pytest.mark.dependency(name="issue_011") @pytest.mark.xdist_group(name="branch_main") def test_package_removed_without_issue(main_env, test_user_client, unique_id): """ Test scenario TC-RM-002: Bypassed package removal detection. """ gitea_env, test_full_repo_name, merge_branch_name = main_env pkg_name = f"pkgRM2_{unique_id}" # Pre-create the org label for package removal so the bot can apply it gitea_env.create_org_label("myproducts", "request/Remove Package", "#ff0000") # 1. Create a new repository in mypool organization gitea_env.setup_repo("mypool", pkg_name) # Add workflow-pr as admin collaborator to the repository so it can rename branches gitea_env._request("PUT", f"repos/mypool/{pkg_name}/collaborators/workflow-pr", json={ "permission": "admin" }) # 4. Get latest commit SHA for pkgRM2 resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches/main") pkg_sha = resp.json()["commit"]["id"] # 5. Read current .gitmodules of myproducts/mySLFO try: resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/contents/.gitmodules") old_gitmodules = base64.b64decode(resp.json()["content"]).decode("utf-8") except Exception: old_gitmodules = "" # 6. Update .gitmodules with our submodule new_gitmodules = old_gitmodules + f"\n[submodule \"{pkg_name}\"]\n path = {pkg_name}\n url = ../../mypool/{pkg_name}.git\n" gitea_env.create_file("myproducts", "mySLFO", ".gitmodules", new_gitmodules, branch="main", message=f"Add submodule {pkg_name}") time.sleep(2) # 7. Add gitlink via diffpatch diff_content = f"""diff --git a/{pkg_name} b/{pkg_name} new file mode 160000 index 00000000..{pkg_sha} --- /dev/null +++ b/{pkg_name} @@ -0,0 +1 @@ +Subproject commit {pkg_sha} """ data = { "branch": "main", "content": diff_content, "message": f"Add {pkg_name} gitlink" } gitea_env._request("POST", "repos/myproducts/mySLFO/diffpatch", json=data) time.sleep(2) # 8. Read the .gitmodules content again resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/contents/.gitmodules") current_gitmodules = base64.b64decode(resp.json()["content"]).decode("utf-8") # Remove our submodule section section_to_remove = f'\n[submodule "{pkg_name}"]\n path = {pkg_name}\n url = ../../mypool/{pkg_name}.git\n' if section_to_remove in current_gitmodules: removed_gitmodules = current_gitmodules.replace(section_to_remove, "") else: # Fallback if whitespace differs removed_gitmodules = current_gitmodules.replace(f'[submodule "{pkg_name}"]', "").replace(f'path = {pkg_name}', "").replace(f'url = ../../mypool/{pkg_name}.git', "") # . Update .gitmodules on the removal branch # 11. Delete submodule gitlink via diffpatch on the removal branch del_diff = f"""diff --git a/{pkg_name} b/{pkg_name} deleted file mode 160000 index {pkg_sha}..00000000 --- a/{pkg_name} +++ /dev/null @@ -1 +0,0 @@ -Subproject commit {pkg_sha} """ data = { "branch": f"main", "content": del_diff, "message": f"Delete {pkg_name} gitlink" } gitea_env._request("POST", "repos/myproducts/mySLFO/diffpatch", json=data) time.sleep(2) # 14. Check if branch in mypool/{pkg_name} was renamed to main-removed renamed = False for _ in range(60): resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches") branches = [b["name"] for b in resp.json()] if "main-removed" in branches and "main" not in branches: renamed = True break time.sleep(2) assert renamed, f"Package branch for {pkg_name} was not renamed to main-removed after manual PR merge" @pytest.mark.t003 @pytest.mark.xdist_group(name="branch_main") def test_issue_processor_readd_package(main_env, test_user_client, ownerB_client, unique_id): """ Test scenario TC-RM-003: Package re-addition after removal. """ gitea_env, test_full_repo_name, merge_branch_name = main_env pkg_name = f"pkgRM3_{unique_id}" # Pre-create the org labels so the bot can apply them gitea_env.create_org_label("myproducts", "request/Remove Package", "#ff0000") gitea_env.create_org_label("myproducts", "request/Add Package", "#00ff00") # 1. Create a new repository in mypool's organization gitea_env.setup_repo("mypool", pkg_name) # Add workflow-pr as admin collaborator to the repository so it can rename/delete branches gitea_env._request("PUT", f"repos/mypool/{pkg_name}/collaborators/workflow-pr", json={ "permission": "admin" }) # 4. Get latest commit SHA for pkgRM3 resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches/main") pkg_sha = resp.json()["commit"]["id"] # 5. Read current .gitmodules of myproducts/mySLFO try: resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/contents/.gitmodules") old_gitmodules = base64.b64decode(resp.json()["content"]).decode("utf-8") except Exception: old_gitmodules = "" # 6. Update .gitmodules with our submodule new_gitmodules = old_gitmodules + f"\n[submodule \"{pkg_name}\"]\n path = {pkg_name}\n url = ../../mypool/{pkg_name}.git\n" gitea_env.create_file("myproducts", "mySLFO", ".gitmodules", new_gitmodules, branch="main", message=f"Add submodule {pkg_name}") # 7. Add gitlink via diffpatch diff_content = f"""diff --git a/{pkg_name} b/{pkg_name} new file mode 160000 index 00000000..{pkg_sha} --- /dev/null +++ b/{pkg_name} @@ -0,0 +1 @@ +Subproject commit {pkg_sha} """ data = { "branch": "main", "content": diff_content, "message": f"Add {pkg_name} gitlink" } gitea_env._request("POST", "repos/myproducts/mySLFO/diffpatch", json=data) # 8. Create [RM] issue in myproducts/mySLFO issue_data = { "title": f"[RM] Remove {pkg_name} from main", "body": f"{pkg_name}", "ref": "refs/heads/main" } response, _ = test_user_client._request("POST", "repos/myproducts/mySLFO/issues", json=issue_data) issue = response.json() issue_number = issue["number"] print(f"Created [RM] issue #{issue_number}") # 9. Wait for project PR for package removal to be created prj_pr_number = None for _ in range(60): resp, _ = gitea_env._request("GET", "repos/myproducts/mySLFO/pulls", params={"state": "open"}) pulls = resp.json() for p in pulls: if p["title"] == f"[RM] Remove packages: {pkg_name}": prj_pr_number = p["number"] break if prj_pr_number: break time.sleep(2) assert prj_pr_number is not None, f"Project PR for package removal of {pkg_name} was not created" # 10. Wait for package branch to be renamed to main-removed renamed = False for _ in range(60): resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches") branches = [b["name"] for b in resp.json()] if "main-removed" in branches and "main" not in branches: renamed = True break time.sleep(2) assert renamed, f"Package branch for {pkg_name} was not renamed to main-removed" # 11. Approve the removal Project PR so it gets merged and closes the issue gitea_env.approve_requested_reviews("myproducts/mySLFO", prj_pr_number) # Wait for Project PR to be merged merged = False for _ in range(60): prj_details = gitea_env.get_pr_details("myproducts/mySLFO", prj_pr_number) if prj_details.get("merged"): merged = True break time.sleep(2) assert merged, "Removal project PR was not merged" # Wait for issue to be closed gitea_env.wait_for_issue_closed("myproducts/mySLFO", issue_number) # 12. Create [ADD] issue to re-add the package (which currently has main-removed branch only) add_issue_data = { "title": f"[ADD] Re-add {pkg_name}", "body": f"mypool/{pkg_name}#main-removed", "ref": "refs/heads/main" } response, _ = test_user_client._request("POST", "repos/myproducts/mySLFO/issues", json=add_issue_data) add_issue = response.json() add_issue_number = add_issue["number"] print(f"Created [ADD] issue #{add_issue_number}") # Wait for the "approval required" comment comment_found = gitea_env.wait_for_comment("myproducts/mySLFO", add_issue_number, "", timeout=120) assert comment_found, f"[Issue myproducts/mySLFO#{add_issue_number}] Bot did not post the approval required comment" # Comment "approve" as ownerB to trigger the package PR and project PR creation ownerB_client.create_issue_comment("myproducts/mySLFO", add_issue_number, "approve") # 13. Wait for package PR to be created (which should succeed by detecting and using main-removed branch) pkg_pr_number = gitea_env.wait_for_package_pr(f"mypool/{pkg_name}", add_issue_number) # 14. Wait for project PR to be created prj_pr2_number = gitea_env.wait_for_project_pr(f"mypool/{pkg_name}", pkg_pr_number) # 15. Merge both PRs for the package re-addition gitea_env.approve_and_wait_merge(f"mypool/{pkg_name}", pkg_pr_number, prj_pr2_number) # 16. Verify that the re-addition successfully completed and deleted the main-removed branch deleted_removed_branch = False for _ in range(60): resp, _ = gitea_env._request("GET", f"repos/mypool/{pkg_name}/branches") branches = [b["name"] for b in resp.json()] if "main" in branches and "main-removed" not in branches: deleted_removed_branch = True break time.sleep(2) assert deleted_removed_branch, f"Former removed branch main-removed was not deleted after merge"