8 Commits

Author SHA256 Message Date
Andrii Nikitin
bf8d1196ba ci: workaround event-publisher unwillingness to retry
All checks were successful
Integration tests / t (pull_request) Successful in 6m18s
Integration tests / t (push) Successful in 6m14s
2026-02-24 18:56:03 +01:00
Andrii Nikitin
0456fc114e t: allow tracking of integration config files
Some checks failed
Integration tests / t (pull_request) Successful in 6m21s
Integration tests / t (push) Has been cancelled
Update .gitignore to exempt .conf files within the integration/ directory
from the global *.conf ignore rule. This ensures that essential test
configurations, like rabbitmq.conf, are preserved and shared across
environments, preventing issues where empty directories were
automatically created by Podman-compose.
2026-02-24 17:17:46 +01:00
Andrii Nikitin
ff68cc8200 ci: Add integration test config 2026-02-24 17:17:42 +01:00
Andrii Nikitin
a814c4ce24 t: Add status of tests to test-plan.md 2026-02-23 11:32:44 +01:00
Andrii Nikitin
aadc7f9d41 t: Add integration test for project PR labels
Itroduces a new integration test to verify the automatic application of
labels to project git pull requests based on the workflow configuration.

Changes:
- Add `create_label` method to Gitea API client for test setup.
- Configure a new `label-test` branch with `StagingAuto` and `ReviewPending`
  label mappings in `conftest.py`.
- Update `setup_users_from_config` to correctly handle `*` reviewer prefix.
- Ensure required labels are created during global test setup.
- Add `staging_bot_client` fixture to simulate bot approvals.
- Enable monitoring for the `label-test` branch in `workflow-pr` service.
- Implement `tests/workflow_pr_label_test.py` to verify label logic.
- Mark `review/Pending` check as xfail due to current implementation issues.
2026-02-23 09:38:14 +01:00
Andrii Nikitin
0598448fdb t: modularize test branch configurations in conftest.py
- Split branch configurations into COMMON and CUSTOM structures to reduce redundancy.
- Implemented dynamic field injection for 'Branch' and 'GitProjectName' using branch keys and placeholders.
- Enhanced the configuration merging logic to automatically synthesize final settings for each test environment.
2026-02-22 21:34:58 +01:00
Andrii Nikitin
2c174b687a t: improve debugging for zypper installation failures in Dockerfiles 2026-02-22 20:48:00 +01:00
Andrii Nikitin
4826d0869a t: consolidate test environment setup and optimize service restarts
- Moved Gitea repository, file, and user creation to a session-scoped global initialization in conftest.py.
 - Implemented content-aware configuration updates to avoid unnecessary workflow-pr service restarts.
 - Add review test_001 to verify reviews are added for both maintainers and config-defined reviewers.
2026-02-22 20:45:55 +01:00
14 changed files with 432 additions and 671 deletions

52
.gitea/workflows/t.yaml Normal file
View File

@@ -0,0 +1,52 @@
name: Integration tests
on:
push:
branches: ['main']
pull_request:
workflow_dispatch:
env:
HOME: /var/lib/gitea-runner
REPO_URL: http://src.opensuse.org//git-workflow/autogits.git
jobs:
t:
runs-on: linux-x86_64
steps:
- name: whoami
run: whoami
- name: pwd
run: pwd
- name: vars
run: |
set | grep GITEA_
- name: Clone
run: |
git clone -q ${{ env.REPO_URL }}
- name: Checkout
run: |
echo ${{ gitea.ref }}
git fetch origin ${{ gitea.ref }}
git checkout FETCH_HEAD
working-directory: ./autogits
- name: Prepare binaries
run: make build
working-directory: ./autogits
- name: Prepare images
run: make build
working-directory: ./autogits/integration
- name: Make sure the pod is down
run: make down
working-directory: ./autogits/integration
- name: Start images
run: make up
working-directory: ./autogits/integration
- name: Run tests
run: py.test-3.11 -v tests
working-directory: ./autogits/integration
- name: Make sure the pod is down
if: always()
run: make down
working-directory: ./autogits/integration

1
.gitignore vendored
View File

@@ -1,5 +1,6 @@
*.osc
*.conf
!/integration/**/*.conf
/integration/gitea-data
/integration/gitea-logs
/integration/rabbitmq-data

View File

@@ -44,7 +44,7 @@ build_container:
# Run tests in topology 1
test_container:
podman run --rm --privileged -t --network integration_gitea-network -e GIWTF_IMAGE_SUFFIX=$(GIWTF_IMAGE_SUFFIX) autogits_integration /usr/bin/bash -c "make build && make up && sleep 25 && pytest -v tests/*"
podman run --rm --privileged -t -e GIWTF_IMAGE_SUFFIX=$(GIWTF_IMAGE_SUFFIX) autogits_integration /usr/bin/bash -c "make build && make up && sleep 25 && pytest -v tests/*"
build_local: AUTO_DETECT_MODE=.local

View File

@@ -10,4 +10,7 @@ echo "!!!!!!!!!!!!!!!! using binary $exe; installed package: $package"
which strings > /dev/null 2>&1 && strings "$exe" | grep -A 2 vcs.revision= | head -4 || :
echo "RABBITMQ_HOST: $RABBITMQ_HOST"
echo "sleep 12 sec to let rabbitmq set up, because the bot currently retries only once"
sleep 12
exec $exe "$@"

View File

@@ -11,7 +11,7 @@ RUN zypper -n install \
openssh \
jq \
devel_Factory_git-workflow:gitea \
&& rm -rf /var/cache/zypp/*
&& rm -rf /var/cache/zypp/* || ( tail -n 1000 /var/log/zypper.log ; exit 1 )
# Copy the minimal set of required files from the local 'container-files' directory
COPY container-files/ /

View File

@@ -0,0 +1,7 @@
listeners.ssl.default = 5671
ssl_options.certfile = /etc/rabbitmq/certs/cert.pem
ssl_options.keyfile = /etc/rabbitmq/certs/key.pem
ssl_options.verify = verify_none
ssl_options.fail_if_no_peer_cert = false
management.load_definitions = /etc/rabbitmq/definitions.json

View File

@@ -53,31 +53,37 @@ The testing will be conducted in a dedicated test environment that mimics the pr
## 5. Test Cases
| Test Case ID | Description | Steps to Reproduce | Expected Results | Priority |
| :--- | :--- | :--- | :--- | :--- |
| **TC-SYNC-001** | **Create ProjectGit PR from PackageGit PR** | 1. Create a new PR in a PackageGit repository. | 1. A new PR is created in the corresponding ProjectGit repository with the title "Forwarded PRs: <package_name>".<br>2. The ProjectGit PR description contains a link to the PackageGit PR (e.g., `PR: org/package_repo!pr_number`).<br>3. The package submodule in the ProjectGit PR points to the PackageGit PR's commit. | High |
| **TC-SYNC-002** | **Update ProjectGit PR from PackageGit PR** | 1. Push a new commit to an existing PackageGit PR. | 1. The corresponding ProjectGit PR's head branch is updated with the new commit. | High |
| **TC-SYNC-003** | **WIP Flag Synchronization** | 1. Mark a PackageGit PR as "Work In Progress".<br>2. Remove the WIP flag from the PackageGit PR. | 1. The corresponding ProjectGit PR is also marked as "Work In Progress".<br>2. The WIP flag on the ProjectGit PR is removed. | Medium |
| **TC-SYNC-004** | **WIP Flag (multiple referenced package PRs)** | 1. Create a ProjectGit PR that references multiple PackageGit PRs.<br>2. Mark one of the PackageGit PRs as "Work In Progress".<br>3. Remove the "Work In Progress" flag from all PackageGit PRs. | 1. The ProjectGit PR is marked as "Work In Progress".<br>2. The "Work In Progress" flag is removed from the ProjectGit PR only after it has been removed from all associated PackageGit PRs. | Medium |
| **TC-SYNC-005** | **NoProjectGitPR = true, edits disabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR without "Allow edits from maintainers" enabled. <br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The bot adds a warning comment to the PackageGit PR explaining that it cannot update the PR. | High |
| **TC-SYNC-006** | **NoProjectGitPR = true, edits enabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR with "Allow edits from maintainers" enabled.<br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The submodule commit on the project PR is updated with the new commit from the PackageGit PR. | High |
| **TC-COMMENT-001** | **Detect duplicate comments** | 1. Create a PackageGit PR.<br>2. Wait for the `workflow-pr` bot to act on the PR.<br>3. Edit the body of the PR to trigger the bot a second time. | 1. The bot should not post a duplicate comment. | High |
| **TC-REVIEW-001** | **Add mandatory reviewers** | 1. Create a new PackageGit PR. | 1. All mandatory reviewers are added to both the PackageGit and ProjectGit PRs. | High |
| **TC-REVIEW-002** | **Add advisory reviewers** | 1. Create a new PackageGit PR with advisory reviewers defined in the configuration. | 1. Advisory reviewers are added to the PR, but their approval is not required for merging. | Medium |
| **TC-REVIEW-003** | **Re-add reviewers** | 1. Push a new commit to a PackageGit PR after it has been approved. | 1. The original reviewers are re-added to the PR. | Medium |
| **TC-REVIEW-004** | **Package PR created by a maintainer** | 1. Create a PackageGit PR from the account of a package maintainer. | 1. No review is requested from other package maintainers. | High |
| **TC-REVIEW-005** | **Package PR created by an external user (approve)** | 1. Create a PackageGit PR from the account of a user who is not a package maintainer.<br>2. One of the package maintainers approves the PR. | 1. All package maintainers are added as reviewers.<br>2. Once one maintainer approves the PR, the other maintainers are removed as reviewers. | High |
| **TC-REVIEW-006** | **Package PR created by an external user (reject)** | 1. Create a PackageGit PR from the account of a user who is not a package maintainer.<br>2. One of the package maintainers rejects the PR. | 1. All package maintainers are added as reviewers.<br>2. Once one maintainer rejects the PR, the other maintainers are removed as reviewers. | High |
| **TC-REVIEW-007** | **Package PR created by a maintainer with ReviewRequired=true** | 1. Set `ReviewRequired = true` in `workflow.config`.<br>2. Create a PackageGit PR from the account of a package maintainer. | 1. A review is requested from other package maintainers if available. | High |
| **TC-MERGE-001** | **Automatic Merge** | 1. Create a PackageGit PR.<br>2. Ensure all mandatory reviews are completed on both project and package PRs. | 1. The PR is automatically merged. | High |
| **TC-MERGE-002** | **ManualMergeOnly with Package Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a package maintainer for that package. | 1. The PR is merged. | High |
| **TC-MERGE-003** | **ManualMergeOnly with unauthorized user** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a user who is not a maintainer for that package. | 1. The PR is not merged. | High |
| **TC-MERGE-004** | **ManualMergeOnly with multiple packages** | 1. Create a ProjectGit PR that references multiple PackageGit PRs with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on each package PR from the account of a package maintainer. | 1. The PR is merged only after "merge ok" is commented on all associated PackageGit PRs. | High |
| **TC-MERGE-005** | **ManualMergeOnly with Project Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a project maintainer. | 1. The PR is merged. | High |
| **TC-MERGE-006** | **ManualMergeProject with Project Maintainer** | 1. Create a PackageGit PR with `ManualMergeProject` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the project PR from the account of a project maintainer. | 1. The PR is merged. | High |
| **TC-MERGE-007** | **ManualMergeProject with unauthorized user** | 1. Create a PackageGit PR with `ManualMergeProject` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the project PR from the account of a package maintainer. | 1. The PR is not merged. | High |
| **TC-CONFIG-001** | **Invalid Configuration** | 1. Provide an invalid `workflow.config` file. | 1. The bot reports an error and does not process any PRs. | High |
| **TC-LABEL-001** | **Apply `staging/Auto` label** | 1. Create a new PackageGit PR. | 1. The `staging/Auto` label is applied to the ProjectGit PR. | High |
| **TC-LABEL-002** | **Apply `review/Pending` label** | 1. Create a new PackageGit PR. | 1. The `review/Pending` label is applied to the ProjectGit PR when there are pending reviews. | Medium |
| **TC-LABEL-003** | **Apply `review/Done` label** | 1. Ensure all mandatory reviews for a PR are completed. | 1. The `review/Done` label is applied to the ProjectGit PR when all mandatory reviews are completed. | Medium |
| Test Case ID | Status | Description | Steps to Reproduce | Expected Results | Priority |
| :--- | :--- | :--- | :--- | :--- | :--- |
| **TC-SYNC-001** | P | **Create ProjectGit PR from PackageGit PR** | 1. Create a new PR in a PackageGit repository. | 1. A new PR is created in the corresponding ProjectGit repository with the title "Forwarded PRs: <package_name>".<br>2. The ProjectGit PR description contains a link to the PackageGit PR (e.g., `PR: org/package_repo!pr_number`).<br>3. The package submodule in the ProjectGit PR points to the PackageGit PR's commit. | High |
| **TC-SYNC-002** | P | **Update ProjectGit PR from PackageGit PR** | 1. Push a new commit to an existing PackageGit PR. | 1. The corresponding ProjectGit PR's head branch is updated with the new commit. | High |
| **TC-SYNC-003** | P | **WIP Flag Synchronization** | 1. Mark a PackageGit PR as "Work In Progress".<br>2. Remove the WIP flag from the PackageGit PR. | 1. The corresponding ProjectGit PR is also marked as "Work In Progress".<br>2. The WIP flag on the ProjectGit PR is removed. | Medium |
| **TC-SYNC-004** | - | **WIP Flag (multiple referenced package PRs)** | 1. Create a ProjectGit PR that references multiple PackageGit PRs.<br>2. Mark one of the PackageGit PRs as "Work In Progress".<br>3. Remove the "Work In Progress" flag from all PackageGit PRs. | 1. The ProjectGit PR is marked as "Work In Progress".<br>2. The "Work In Progress" flag is removed from the ProjectGit PR only after it has been removed from all associated PackageGit PRs. | Medium |
| **TC-SYNC-005** | x | **NoProjectGitPR = true, edits disabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR without "Allow edits from maintainers" enabled. <br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The bot adds a warning comment to the PackageGit PR explaining that it cannot update the PR. | High |
| **TC-SYNC-006** | x | **NoProjectGitPR = true, edits enabled** | 1. Set `NoProjectGitPR = true` in `workflow.config`.<br>2. Create a PackageGit PR with "Allow edits from maintainers" enabled.<br>3. Push a new commit to the PackageGit PR. | 1. No ProjectGit PR is created.<br>2. The submodule commit on the project PR is updated with the new commit from the PackageGit PR. | High |
| **TC-COMMENT-001** | - | **Detect duplicate comments** | 1. Create a PackageGit PR.<br>2. Wait for the `workflow-pr` bot to act on the PR.<br>3. Edit the body of the PR to trigger the bot a second time. | 1. The bot should not post a duplicate comment. | High |
| **TC-REVIEW-001** | P | **Add mandatory reviewers** | 1. Create a new PackageGit PR. | 1. All mandatory reviewers are added to both the PackageGit and ProjectGit PRs. | High |
| **TC-REVIEW-002** | - | **Add advisory reviewers** | 1. Create a new PackageGit PR with advisory reviewers defined in the configuration. | 1. Advisory reviewers are added to the PR, but their approval is not required for merging. | Medium |
| **TC-REVIEW-003** | - | **Re-add reviewers** | 1. Push a new commit to a PackageGit PR after it has been approved. | 1. The original reviewers are re-added to the PR. | Medium |
| **TC-REVIEW-004** | x | **Package PR created by a maintainer** | 1. Create a PackageGit PR from the account of a package maintainer. | 1. No review is requested from other package maintainers. | High |
| **TC-REVIEW-005** | P | **Package PR created by an external user (approve)** | 1. Create a PackageGit PR from the account of a user who is not a package maintainer.<br>2. One of the package maintainers approves the PR. | 1. All package maintainers are added as reviewers.<br>2. Once one maintainer approves the PR, the other maintainers are removed as reviewers. | High |
| **TC-REVIEW-006** | P | **Package PR created by an external user (reject)** | 1. Create a PackageGit PR from the account of a user who is not a package maintainer.<br>2. One of the package maintainers rejects the PR. | 1. All package maintainers are added as reviewers.<br>2. Once one maintainer rejects the PR, the other maintainers are removed as reviewers. | High |
| **TC-REVIEW-007** | P | **Package PR created by a maintainer with ReviewRequired=true** | 1. Set `ReviewRequired = true` in `workflow.config`.<br>2. Create a PackageGit PR from the account of a package maintainer. | 1. A review is requested from other package maintainers if available. | High |
| **TC-MERGE-001** | x | **Automatic Merge** | 1. Create a PackageGit PR.<br>2. Ensure all mandatory reviews are completed on both project and package PRs. | 1. The PR is automatically merged. | High |
| **TC-MERGE-002** | - | **ManualMergeOnly with Package Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a package maintainer for that package. | 1. The PR is merged. | High |
| **TC-MERGE-003** | - | **ManualMergeOnly with unauthorized user** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a user who is not a maintainer for that package. | 1. The PR is not merged. | High |
| **TC-MERGE-004** | - | **ManualMergeOnly with multiple packages** | 1. Create a ProjectGit PR that references multiple PackageGit PRs with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on each package PR from the account of a package maintainer. | 1. The PR is merged only after "merge ok" is commented on all associated PackageGit PRs. | High |
| **TC-MERGE-005** | - | **ManualMergeOnly with Project Maintainer** | 1. Create a PackageGit PR with `ManualMergeOnly` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the package PR from the account of a project maintainer. | 1. The PR is merged. | High |
| **TC-MERGE-006** | - | **ManualMergeProject with Project Maintainer** | 1. Create a PackageGit PR with `ManualMergeProject` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the project PR from the account of a project maintainer. | 1. The PR is merged. | High |
| **TC-MERGE-007** | - | **ManualMergeProject with unauthorized user** | 1. Create a PackageGit PR with `ManualMergeProject` set to `true`.<br>2. Ensure all mandatory reviews are completed on both project and package PRs.<br>3. Comment "merge ok" on the project PR from the account of a package maintainer. | 1. The PR is not merged. | High |
| **TC-CONFIG-001** | - | **Invalid Configuration** | 1. Provide an invalid `workflow.config` file. | 1. The bot reports an error and does not process any PRs. | High |
| **TC-LABEL-001** | P | **Apply `staging/Auto` label** | 1. Create a new PackageGit PR. | 1. The `staging/Auto` label is applied to the ProjectGit PR. | High |
| **TC-LABEL-002** | x | **Apply `review/Pending` label** | 1. Create a new PackageGit PR. | 1. The `review/Pending` label is applied to the ProjectGit PR when there are pending reviews. | Medium |
| **TC-LABEL-003** | - | **Apply `review/Done` label** | 1. Ensure all mandatory reviews for a PR are completed. | 1. The `review/Done` label is applied to the ProjectGit PR when all mandatory reviews are completed. | Medium |
#### Legend:
* P = implemented and passing;
* x = likely implemented, but investigation is needed;
* X = implemented and likely to pass, but someteimes may fail, but troubleshooting is needed;
* - = test is not implemented

View File

@@ -6,232 +6,77 @@ import pytest
import requests
import time
import os
# Assuming GiteaAPIClient is in tests/lib/common_test_utils.py
import json
import base64
from tests.lib.common_test_utils import GiteaAPIClient
@pytest.fixture(scope="session")
def gitea_env():
"""
Sets up the Gitea environment with dummy data and provides a GiteaAPIClient instance.
"""
gitea_url = "http://127.0.0.1:3000"
# Read admin token
admin_token_path = "./gitea-data/admin.token" # Corrected path
admin_token = None
try:
with open(admin_token_path, "r") as f:
admin_token = f.read().strip()
except FileNotFoundError:
raise Exception(f"Admin token file not found at {admin_token_path}. Ensure it's generated and accessible.")
# Headers for authenticated requests
auth_headers = {"Authorization": f"token {admin_token}", "Content-Type": "application/json"}
# Wait for Gitea to be available
print(f"Waiting for Gitea at {gitea_url}...")
max_retries = 5
for i in range(max_retries):
try:
# Check a specific API endpoint that indicates readiness
response = requests.get(f"{gitea_url}/api/v1/version", headers=auth_headers, timeout=5)
if response.status_code == 200:
print("Gitea API is available.")
break
except requests.exceptions.ConnectionError:
pass
print(f"Gitea not ready ({response.status_code if 'response' in locals() else 'ConnectionError'}), retrying in 1 seconds... ({i+1}/{max_retries})")
time.sleep(1)
else:
raise Exception("Gitea did not become available within the expected time.")
client = GiteaAPIClient(base_url=gitea_url, token=admin_token)
# Setup dummy data
print("--- Starting Gitea Dummy Data Setup from Pytest Fixture ---")
client.create_org("products")
client.create_org("pool")
client.create_repo("products", "SLFO")
client.create_repo("pool", "pkgA")
client.create_repo("pool", "pkgB")
# The add_submodules method also creates workflow.config and staging.config
client.add_submodules("products", "SLFO")
time.sleep(1)
workflow_config_content = """{
BRANCH_CONFIG_COMMON = {
"workflow.config": {
"Workflows": ["pr"],
"GitProjectName": "products/SLFO#main",
"Organization": "pool",
"Branch": "main",
"ManualMergeProject": true,
"Reviewers": [ "-autogits_obs_staging_bot" ]
}"""
client.create_file("products", "SLFO", "workflow.config", workflow_config_content)
"Reviewers": ["-autogits_obs_staging_bot"],
"GitProjectName": "products/SLFO#{branch}"
},
"_maintainership.json": {
"": ["ownerX", "ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB", "ownerBB"]
}
}
staging_config_content = """{
"ObsProject": "openSUSE:Leap:16.0",
"StagingProject": "openSUSE:Leap:16.0:PullRequest"
}"""
client.create_file("products", "SLFO", "staging.config", staging_config_content)
BRANCH_CONFIG_CUSTOM = {
"main": {
"workflow.config": {
"ManualMergeProject": True
},
"staging.config": {
"ObsProject": "openSUSE:Leap:16.0",
"StagingProject": "openSUSE:Leap:16.0:PullRequest"
}
},
"merge": {
"workflow.config": {
"Reviewers": ["+usera", "+userb", "-autogits_obs_staging_bot"]
}
},
"maintainer-merge": {
"workflow.config": {
}
},
"review-required": {
"workflow.config": {
"ReviewRequired": True
}
},
"dev": {
"workflow.config": {
"ManualMergeProject": True,
"NoProjectGitPR": True
}
},
"label-test": {
"workflow.config": {
"ManualMergeProject": True,
"Reviewers": ["*usera"],
"ReviewRequired": True,
"Labels": {
"StagingAuto": "staging/Backlog",
"ReviewPending": "review/Pending"
}
}
}
}
client.add_collaborator("products", "SLFO", "autogits_obs_staging_bot", "write")
client.add_collaborator("products", "SLFO", "workflow-pr", "write")
client.add_collaborator("pool", "pkgA", "workflow-pr", "write")
client.add_collaborator("pool", "pkgB", "workflow-pr", "write")
client.update_repo_settings("products", "SLFO")
client.update_repo_settings("pool", "pkgA")
client.update_repo_settings("pool", "pkgB")
print("--- Gitea Dummy Data Setup Complete ---")
time.sleep(1) # Give workflow-pr bot time to become fully active
yield client
@pytest.fixture(scope="session")
def configured_dev_branch_env(gitea_env: GiteaAPIClient, request):
"""
Fixture to set up a 'dev' branch in products/SLFO and pool/pkgA,
and configure workflow.config in products/SLFO#dev with specific content.
Yields (gitea_env, test_full_repo_name, dev_branch_name).
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
dev_branch_name = "dev"
workflow_config_content = request.param # Get config content from parametrization
print(f"--- Setting up 'dev' branch and workflow.config in {test_full_repo_name}#{dev_branch_name} ---")
# Get the latest commit SHA of the main branch
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create 'dev' branch from 'main' in products/SLFO
gitea_env.create_branch(test_org_name, test_repo_name, dev_branch_name, main_branch_sha)
# Create 'dev' branch in pool/pkgA as well
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
gitea_env.create_branch("pool", "pkgA", dev_branch_name, pool_pkga_main_sha)
# Create 'dev' branch in pool/pkgB as well
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
gitea_env.create_branch("pool", "pkgB", dev_branch_name, pool_pkgb_main_sha)
# Create/update workflow.config with the provided content
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", workflow_config_content, branch=dev_branch_name)
print(f"Created workflow.config with specific content in {test_full_repo_name}#{dev_branch_name}")
# Restart workflow-pr service to pick up new project config
gitea_env.restart_service("workflow-pr")
time.sleep(1) # Give the service time to restart and re-initialize
yield gitea_env, test_full_repo_name, dev_branch_name
# Teardown (optional, depending on test strategy)
# For now, we'll leave resources for inspection. If a clean slate is needed for each test,
# this fixture's scope would be 'function' and teardown logic would be added here.
@pytest.fixture(scope="session")
def no_project_git_pr_env(gitea_env: GiteaAPIClient):
"""
Sets up 'dev' branch in products/SLFO and pool/pkgA,
and configures workflow.config in products/SLFO#dev with NoProjectGitPR: true.
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
dev_branch_name = "dev"
print(f"--- Setting up workflow.config in {test_full_repo_name}#{dev_branch_name} for No Project PR ---")
# Get the latest commit SHA of the main branch
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create 'dev' branch from 'main' in products/SLFO
try:
gitea_env.create_branch(test_org_name, test_repo_name, dev_branch_name, main_branch_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'dev' branch in pool/pkgA as well
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgA", dev_branch_name, pool_pkga_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'dev' branch in pool/pkgB as well
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgB", dev_branch_name, pool_pkgb_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Setup workflow.config to have "NoProjectGitPR": true
workflow_config_content_no_project_pr = f"""{{
"Workflows": ["pr"],
"GitProjectName": "{test_full_repo_name}#{dev_branch_name}",
"Organization": "pool",
"Branch": "dev",
"ManualMergeProject": true,
"Reviewers": [ "-autogits_obs_staging_bot" ],
"NoProjectGitPR": true
}}"""
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", workflow_config_content_no_project_pr, branch=dev_branch_name)
print(f"Created workflow.config with NoProjectGitPR: true in {test_full_repo_name}#{dev_branch_name}")
# Restart workflow-pr service
gitea_env.restart_service("workflow-pr")
time.sleep(1) # Give the service time to restart and re-initialize
return gitea_env, test_full_repo_name, dev_branch_name
@pytest.fixture(scope="session")
def test_user_client(gitea_env: GiteaAPIClient):
"""
Creates a new unique user and returns a GiteaAPIClient instance for them using sudo.
This user should not have write permissions to the test repositories by default.
"""
username = f"user-{int(time.time())}"
password = "password123"
email = f"{username}@example.com"
gitea_env.create_user(username, password, email)
# Grant write access to pool/pkgA
gitea_env.add_collaborator("pool", "pkgA", username, "write")
# Use admin token with Sudo header
admin_token = gitea_env.headers["Authorization"].split(" ")[1]
return GiteaAPIClient(base_url=gitea_env.base_url, token=admin_token, sudo=username)
def setup_users_from_config(client: GiteaAPIClient, workflow_config: str, maintainership_config: str):
def setup_users_from_config(client: GiteaAPIClient, wf: dict, mt: dict):
"""
Parses workflow.config and _maintainership.json, creates users, and adds them as collaborators.
"""
import json
wf = json.loads(workflow_config)
mt = json.loads(maintainership_config)
all_users = set()
# Extract from workflow.config Reviewers
reviewers = wf.get("Reviewers", [])
for r in reviewers:
# Strip +, - prefixes
username = r.lstrip("+-")
username = r.lstrip("+-*")
if username and username not in ["autogits_obs_staging_bot", "workflow-pr"]:
all_users.add(username)
@@ -243,8 +88,6 @@ def setup_users_from_config(client: GiteaAPIClient, workflow_config: str, mainta
# Create all users
for username in all_users:
client.create_user(username, "password123", f"{username}@example.com")
# Global maintainers (empty key) get write access to everything
# Actually, let's just make them collaborators on SLFO, pkgA, pkgB for simplicity in tests
client.add_collaborator("products", "SLFO", username, "write")
# Set specific repository permissions based on maintainership
@@ -252,469 +95,167 @@ def setup_users_from_config(client: GiteaAPIClient, workflow_config: str, mainta
repo_name = pkg if pkg else None
for username in users:
if not repo_name:
# Global maintainer - already added to SLFO, add to pkgA/pkgB
client.add_collaborator("pool", "pkgA", username, "write")
client.add_collaborator("pool", "pkgB", username, "write")
else:
client.add_collaborator("pool", repo_name, username, "write")
def ensure_config_file(client: GiteaAPIClient, owner: str, repo: str, branch: str, file_name: str, expected_content_dict: dict):
"""
Checks if a config file exists and has the correct content.
Returns True if a change was made, False otherwise.
"""
file_info = client.get_file_info(owner, repo, file_name, branch=branch)
expected_content = json.dumps(expected_content_dict, indent=4)
if file_info:
current_content_raw = base64.b64decode(file_info["content"]).decode("utf-8")
try:
current_content_dict = json.loads(current_content_raw)
if current_content_dict == expected_content_dict:
return False
except json.JSONDecodeError:
pass # Overwrite invalid JSON
client.create_file(owner, repo, file_name, expected_content, branch=branch)
return True
@pytest.fixture(scope="session")
def gitea_env():
"""
Sets up the Gitea environment with dummy data and provides a GiteaAPIClient instance.
Global fixture to set up the Gitea environment for all tests.
"""
gitea_url = "http://127.0.0.1:3000"
admin_token_path = "./gitea-data/admin.token"
# Read admin token
admin_token_path = "./gitea-data/admin.token" # Corrected path
admin_token = None
try:
with open(admin_token_path, "r") as f:
admin_token = f.read().strip()
except FileNotFoundError:
raise Exception(f"Admin token file not found at {admin_token_path}. Ensure it's generated and accessible.")
# Headers for authenticated requests
auth_headers = {"Authorization": f"token {admin_token}", "Content-Type": "application/json"}
# Wait for Gitea to be available
print(f"Waiting for Gitea at {gitea_url}...")
max_retries = 5
for i in range(max_retries):
try:
# Check a specific API endpoint that indicates readiness
response = requests.get(f"{gitea_url}/api/v1/version", headers=auth_headers, timeout=5)
if response.status_code == 200:
print("Gitea API is available.")
break
except requests.exceptions.ConnectionError:
pass
print(f"Gitea not ready ({response.status_code if 'response' in locals() else 'ConnectionError'}), retrying in 1 seconds... ({i+1}/{max_retries})")
time.sleep(1)
else:
raise Exception("Gitea did not become available within the expected time.")
raise Exception(f"Admin token file not found at {admin_token_path}.")
client = GiteaAPIClient(base_url=gitea_url, token=admin_token)
# Setup dummy data
print("--- Starting Gitea Dummy Data Setup from Pytest Fixture ---")
# Wait for Gitea
for i in range(10):
try:
if client._request("GET", "version").status_code == 200:
break
except:
pass
time.sleep(1)
else:
raise Exception("Gitea not available.")
print("--- Starting Gitea Global Setup ---")
client.create_org("products")
client.create_org("pool")
client.create_repo("products", "SLFO")
client.create_repo("pool", "pkgA")
client.create_repo("pool", "pkgB")
# The add_submodules method also creates workflow.config and staging.config
client.update_repo_settings("products", "SLFO")
client.update_repo_settings("pool", "pkgA")
client.update_repo_settings("pool", "pkgB")
# Create labels
client.create_label("products", "SLFO", "staging/Backlog", color="#0000ff")
client.create_label("products", "SLFO", "review/Pending", color="#ffff00")
# Submodules in SLFO
client.add_submodules("products", "SLFO")
time.sleep(1)
workflow_config_content = """{
"Workflows": ["pr"],
"GitProjectName": "products/SLFO#main",
"Organization": "pool",
"Branch": "main",
"ManualMergeProject": true,
"Reviewers": [ "-autogits_obs_staging_bot" ]
}"""
client.create_file("products", "SLFO", "workflow.config", workflow_config_content)
staging_config_content = """{
"ObsProject": "openSUSE:Leap:16.0",
"StagingProject": "openSUSE:Leap:16.0:PullRequest"
}"""
client.create_file("products", "SLFO", "staging.config", staging_config_content)
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
# Create users from default main config
setup_users_from_config(client, workflow_config_content, maintainership_content)
client.add_collaborator("products", "SLFO", "autogits_obs_staging_bot", "write")
client.add_collaborator("products", "SLFO", "workflow-pr", "write")
client.add_collaborator("pool", "pkgA", "workflow-pr", "write")
client.add_collaborator("pool", "pkgB", "workflow-pr", "write")
client.update_repo_settings("products", "SLFO")
client.update_repo_settings("pool", "pkgA")
client.update_repo_settings("pool", "pkgB")
print("--- Gitea Dummy Data Setup Complete ---")
time.sleep(1) # Give workflow-pr bot time to become fully active
restart_needed = False
# Setup all branches and configs
for branch_name, custom_configs in BRANCH_CONFIG_CUSTOM.items():
# Ensure branch exists in all 3 repos
for owner, repo in [("products", "SLFO"), ("pool", "pkgA"), ("pool", "pkgB")]:
if branch_name != "main":
try:
main_sha = client._request("GET", f"repos/{owner}/{repo}/branches/main").json()["commit"]["id"]
client.create_branch(owner, repo, branch_name, main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Merge configs
merged_configs = {}
for file_name, common_content in BRANCH_CONFIG_COMMON.items():
merged_configs[file_name] = common_content.copy()
# Dynamically format values containing {branch}
if file_name == "workflow.config":
if "GitProjectName" in merged_configs[file_name]:
merged_configs[file_name]["GitProjectName"] = merged_configs[file_name]["GitProjectName"].format(branch=branch_name)
# Inject branch name dynamically
merged_configs[file_name]["Branch"] = branch_name
for file_name, custom_content in custom_configs.items():
if file_name in merged_configs:
merged_configs[file_name].update(custom_content)
else:
merged_configs[file_name] = custom_content
# Ensure config files in products/SLFO
for file_name, content_dict in merged_configs.items():
if ensure_config_file(client, "products", "SLFO", branch_name, file_name, content_dict):
restart_needed = True
# Setup users (using configs from this branch)
setup_users_from_config(client, merged_configs.get("workflow.config", {}), merged_configs.get("_maintainership.json", {}))
if restart_needed:
client.restart_service("workflow-pr")
time.sleep(2) # Give it time to pick up changes
print("--- Gitea Global Setup Complete ---")
yield client
@pytest.fixture(scope="session")
def automerge_env(gitea_env):
return gitea_env, "products/SLFO", "merge"
@pytest.fixture(scope="session")
def configured_dev_branch_env(gitea_env: GiteaAPIClient, request):
"""
Fixture to set up a 'dev' branch in products/SLFO and pool/pkgA,
and configure workflow.config in products/SLFO#dev with specific content.
Yields (gitea_env, test_full_repo_name, dev_branch_name).
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
dev_branch_name = "dev"
workflow_config_content = request.param # Get config content from parametrization
print(f"--- Setting up 'dev' branch and workflow.config in {test_full_repo_name}#{dev_branch_name} ---")
# Get the latest commit SHA of the main branch
gitea_env.ensure_branch_exists(test_org_name, test_repo_name, "main")
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create 'dev' branch from 'main' in products/SLFO
gitea_env.create_branch(test_org_name, test_repo_name, dev_branch_name, main_branch_sha)
# Create 'dev' branch in pool/pkgA as well
gitea_env.ensure_branch_exists("pool", "pkgA", "main")
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
gitea_env.create_branch("pool", "pkgA", dev_branch_name, pool_pkga_main_sha)
# Create 'dev' branch in pool/pkgB as well
gitea_env.ensure_branch_exists("pool", "pkgB", "main")
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
gitea_env.create_branch("pool", "pkgB", dev_branch_name, pool_pkgb_main_sha)
# Create/update workflow.config with the provided content
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", workflow_config_content, branch=dev_branch_name)
# For this fixture, we use default maintainership as we don't receive it in request.param
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
setup_users_from_config(gitea_env, workflow_config_content, maintainership_content)
print(f"Created workflow.config with specific content in {test_full_repo_name}#{dev_branch_name}")
# Restart workflow-pr service to pick up new project config
gitea_env.restart_service("workflow-pr")
time.sleep(1) # Give the service time to restart and re-initialize
yield gitea_env, test_full_repo_name, dev_branch_name
def maintainer_env(gitea_env):
return gitea_env, "products/SLFO", "maintainer-merge"
@pytest.fixture(scope="session")
def no_project_git_pr_env(gitea_env: GiteaAPIClient):
"""
Sets up 'dev' branch in products/SLFO and pool/pkgA,
and configures workflow.config in products/SLFO#dev with NoProjectGitPR: true.
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
dev_branch_name = "dev"
print(f"--- Setting up workflow.config in {test_full_repo_name}#{dev_branch_name} for No Project PR ---")
# Get the latest commit SHA of the main branch
gitea_env.ensure_branch_exists(test_org_name, test_repo_name, "main")
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create 'dev' branch from 'main' in products/SLFO
try:
gitea_env.create_branch(test_org_name, test_repo_name, dev_branch_name, main_branch_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'dev' branch in pool/pkgA as well
gitea_env.ensure_branch_exists("pool", "pkgA", "main")
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgA", dev_branch_name, pool_pkga_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'dev' branch in pool/pkgB as well
gitea_env.ensure_branch_exists("pool", "pkgB", "main")
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgB", dev_branch_name, pool_pkgb_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Setup workflow.config to have "NoProjectGitPR": true
workflow_config_content = f"""{{
"Workflows": ["pr"],
"GitProjectName": "{test_full_repo_name}#{dev_branch_name}",
"Organization": "pool",
"Branch": "dev",
"ManualMergeProject": true,
"Reviewers": [ "-autogits_obs_staging_bot" ],
"NoProjectGitPR": true
}}"""
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", workflow_config_content, branch=dev_branch_name)
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
setup_users_from_config(gitea_env, workflow_config_content, maintainership_content)
print(f"Created workflow.config with NoProjectGitPR: true in {test_full_repo_name}#{dev_branch_name}")
# Restart workflow-pr service
gitea_env.restart_service("workflow-pr")
time.sleep(1) # Give the service time to restart and re-initialize
return gitea_env, test_full_repo_name, dev_branch_name
def review_required_env(gitea_env):
return gitea_env, "products/SLFO", "review-required"
@pytest.fixture(scope="session")
def test_user_client(gitea_env: GiteaAPIClient):
"""
Creates a new unique user and returns a GiteaAPIClient instance for them using sudo.
This user should not have write permissions to the test repositories by default.
"""
username = f"user-{int(time.time())}"
password = "password123"
email = f"{username}@example.com"
gitea_env.create_user(username, password, email)
# Grant write access to pool/pkgA
def no_project_git_pr_env(gitea_env):
return gitea_env, "products/SLFO", "dev"
@pytest.fixture(scope="session")
def label_env(gitea_env):
return gitea_env, "products/SLFO", "label-test"
@pytest.fixture(scope="session")
def ownerA_client(gitea_env):
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo="ownerA")
@pytest.fixture(scope="session")
def ownerB_client(gitea_env):
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo="ownerB")
@pytest.fixture(scope="session")
def ownerBB_client(gitea_env):
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo="ownerBB")
@pytest.fixture(scope="session")
def staging_bot_client(gitea_env):
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo="autogits_obs_staging_bot")
@pytest.fixture(scope="session")
def test_user_client(gitea_env):
username = f"test-user-{int(time.time())}"
gitea_env.create_user(username, "password123", f"{username}@example.com")
gitea_env.add_collaborator("pool", "pkgA", username, "write")
# Use admin token with Sudo header
admin_token = gitea_env.headers["Authorization"].split(" ")[1]
return GiteaAPIClient(base_url=gitea_env.base_url, token=admin_token, sudo=username)
@pytest.fixture(scope="session")
def automerge_env(gitea_env: GiteaAPIClient):
"""
Sets up 'merge' branch and custom workflow.config for automerge tests.
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
merge_branch_name = "merge"
print(f"--- Setting up '{merge_branch_name}' branch and workflow.config in {test_full_repo_name} ---")
# Get the latest commit SHA of the main branch
gitea_env.ensure_branch_exists(test_org_name, test_repo_name, "main")
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create 'merge' branch from 'main' in products/SLFO
try:
gitea_env.create_branch(test_org_name, test_repo_name, merge_branch_name, main_branch_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'merge' branch in pool/pkgA as well
gitea_env.ensure_branch_exists("pool", "pkgA", "main")
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgA", merge_branch_name, pool_pkga_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create 'merge' branch in pool/pkgB as well
gitea_env.ensure_branch_exists("pool", "pkgB", "main")
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgB", merge_branch_name, pool_pkgb_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
custom_workflow_config = f"""{{
"Workflows": ["pr"],
"GitProjectName": "{test_full_repo_name}#{merge_branch_name}",
"Organization": "pool",
"Branch": "{merge_branch_name}",
"Reviewers": [ "+usera", "+userb", "-autogits_obs_staging_bot" ]
}}"""
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", custom_workflow_config, branch=merge_branch_name)
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
gitea_env.create_file(test_org_name, test_repo_name, "_maintainership.json", maintainership_content, branch=merge_branch_name)
setup_users_from_config(gitea_env, custom_workflow_config, maintainership_content)
# Restart workflow-pr service
gitea_env.restart_service("workflow-pr")
time.sleep(1)
return gitea_env, test_full_repo_name, merge_branch_name
@pytest.fixture(scope="session")
def maintainer_env(gitea_env: GiteaAPIClient):
"""
Sets up 'maintainer-merge' branch and workflow.config without mandatory reviewers.
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
branch_name = "maintainer-merge"
print(f"--- Setting up '{branch_name}' branch and workflow.config in {test_full_repo_name} ---")
# Get the latest commit SHA of the main branch
gitea_env.ensure_branch_exists(test_org_name, test_repo_name, "main")
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create branch in products/SLFO
try:
gitea_env.create_branch(test_org_name, test_repo_name, branch_name, main_branch_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create branch in pool/pkgA
gitea_env.ensure_branch_exists("pool", "pkgA", "main")
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgA", branch_name, pool_pkga_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create branch in pool/pkgB
gitea_env.ensure_branch_exists("pool", "pkgB", "main")
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgB", branch_name, pool_pkgb_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
custom_workflow_config = f"""{{
"Workflows": ["pr"],
"GitProjectName": "{test_full_repo_name}#{branch_name}",
"Organization": "pool",
"Branch": "{branch_name}",
"Reviewers": [ "-autogits_obs_staging_bot" ]
}}"""
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", custom_workflow_config, branch=branch_name)
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
gitea_env.create_file(test_org_name, test_repo_name, "_maintainership.json", maintainership_content, branch=branch_name)
setup_users_from_config(gitea_env, custom_workflow_config, maintainership_content)
gitea_env.add_collaborator(test_org_name, test_repo_name, "autogits_obs_staging_bot", "write")
# Restart workflow-pr service
gitea_env.restart_service("workflow-pr")
time.sleep(1)
return gitea_env, test_full_repo_name, branch_name
@pytest.fixture(scope="session")
def review_required_env(gitea_env: GiteaAPIClient):
"""
Sets up 'review-required' branch and workflow.config with ReviewRequired: true.
"""
test_org_name = "products"
test_repo_name = "SLFO"
test_full_repo_name = f"{test_org_name}/{test_repo_name}"
branch_name = "review-required"
print(f"--- Setting up '{branch_name}' branch and workflow.config in {test_full_repo_name} ---")
# Get the latest commit SHA of the main branch
gitea_env.ensure_branch_exists(test_org_name, test_repo_name, "main")
main_branch_sha = gitea_env._request("GET", f"repos/{test_org_name}/{test_repo_name}/branches/main").json()["commit"]["id"]
# Create branch in products/SLFO
try:
gitea_env.create_branch(test_org_name, test_repo_name, branch_name, main_branch_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create branch in pool/pkgA
gitea_env.ensure_branch_exists("pool", "pkgA", "main")
pool_pkga_main_sha = gitea_env._request("GET", "repos/pool/pkgA/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgA", branch_name, pool_pkga_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
# Create branch in pool/pkgB
gitea_env.ensure_branch_exists("pool", "pkgB", "main")
pool_pkgb_main_sha = gitea_env._request("GET", "repos/pool/pkgB/branches/main").json()["commit"]["id"]
try:
gitea_env.create_branch("pool", "pkgB", branch_name, pool_pkgb_main_sha)
except Exception as e:
if "already exists" not in str(e).lower():
raise
custom_workflow_config = f"""{{
"Workflows": ["pr"],
"GitProjectName": "{test_full_repo_name}#{branch_name}",
"Organization": "pool",
"Branch": "{branch_name}",
"Reviewers": [ "-autogits_obs_staging_bot" ],
"ReviewRequired": true
}}"""
gitea_env.create_file(test_org_name, test_repo_name, "workflow.config", custom_workflow_config, branch=branch_name)
maintainership_content = """{
"": ["ownerX","ownerY"],
"pkgA": ["ownerA"],
"pkgB": ["ownerB","ownerBB"]
}"""
gitea_env.create_file(test_org_name, test_repo_name, "_maintainership.json", maintainership_content, branch=branch_name)
setup_users_from_config(gitea_env, custom_workflow_config, maintainership_content)
gitea_env.add_collaborator(test_org_name, test_repo_name, "autogits_obs_staging_bot", "write")
# Restart workflow-pr service
gitea_env.restart_service("workflow-pr")
time.sleep(1)
return gitea_env, test_full_repo_name, branch_name
@pytest.fixture(scope="session")
def ownerA_client(gitea_env: GiteaAPIClient):
"""
Returns a GiteaAPIClient instance for ownerA.
"""
admin_token = gitea_env.headers["Authorization"].split(" ")[1]
return GiteaAPIClient(base_url=gitea_env.base_url, token=admin_token, sudo="ownerA")
@pytest.fixture(scope="session")
def ownerB_client(gitea_env: GiteaAPIClient):
"""
Returns a GiteaAPIClient instance for ownerB.
"""
admin_token = gitea_env.headers["Authorization"].split(" ")[1]
return GiteaAPIClient(base_url=gitea_env.base_url, token=admin_token, sudo="ownerB")
@pytest.fixture(scope="session")
def ownerBB_client(gitea_env: GiteaAPIClient):
"""
Returns a GiteaAPIClient instance for ownerBB.
"""
admin_token = gitea_env.headers["Authorization"].split(" ")[1]
return GiteaAPIClient(base_url=gitea_env.base_url, token=admin_token, sudo="ownerBB")
gitea_env.add_collaborator("products", "SLFO", username, "write")
return GiteaAPIClient(base_url=gitea_env.base_url, token=gitea_env.headers["Authorization"].split(" ")[1], sudo=username)

View File

@@ -226,6 +226,21 @@ index 0000000..{pkg_b_sha}
self._request("PATCH", f"repos/{org_name}/{repo_name}", json=repo_data)
print(f"Repository settings for '{org_name}/{repo_name}' updated.")
def create_label(self, owner: str, repo: str, name: str, color: str = "#abcdef"):
print(f"--- Creating label '{name}' in {owner}/{repo} ---")
url = f"repos/{owner}/{repo}/labels"
data = {
"name": name,
"color": color
}
try:
self._request("POST", url, json=data)
print(f"Label '{name}' created.")
except requests.exceptions.HTTPError as e:
if e.response.status_code == 422: # Already exists
print(f"Label '{name}' already exists.")
else:
raise
def create_file(self, owner: str, repo: str, file_path: str, content: str, branch: str = "main", message: str = "Add file"):
file_info = self.get_file_info(owner, repo, file_path, branch=branch)

View File

@@ -0,0 +1,97 @@
import pytest
import re
import time
from pathlib import Path
from tests.lib.common_test_utils import (
GiteaAPIClient,
)
# =============================================================================
# TEST CASES
# =============================================================================
@pytest.mark.t001
@pytest.mark.xfail(reason="review pending label is not applied")
def test_001_project_pr_labels(label_env, staging_bot_client):
"""
Test scenario:
1. Setup custom workflow.config with Labels: { "StagingAuto": "staging/Backlog", "ReviewPending": "review/Pending" }.
2. Create a package PR in 'label-test' branch.
3. Make sure the workflow-pr service created related project PR in 'label-test' branch.
4. Wait for the project PR to have the label "staging/Backlog".
5. Post approval from autogits_obs_staging_bot.
6. Check that the project PR gets the label "review/Pending".
"""
gitea_env, test_full_repo_name, branch_name = label_env
# 1. Create a package PR
diff = """diff --git a/label_test_fixture.txt b/label_test_fixture.txt
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgA on branch {branch_name} ---")
package_pr = gitea_env.create_gitea_pr("pool/pkgA", diff, "Test Labels Fixture", False, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgA#{package_pr_number}")
# 2. Make sure the workflow-pr service created related project PR
project_pr_number = None
print(f"Polling pool/pkgA PR #{package_pr_number} timeline for forwarded PR event...")
for _ in range(40):
time.sleep(1)
timeline_events = gitea_env.get_timeline_events("pool/pkgA", package_pr_number)
for event in timeline_events:
if event.get("type") == "pull_ref":
if not (ref_issue := event.get("ref_issue")):
continue
url_to_check = ref_issue.get("html_url", "")
match = re.search(r"products/SLFO/pulls/(\d+)", url_to_check)
if match:
project_pr_number = int(match.group(1))
break
if project_pr_number:
break
assert project_pr_number is not None, "Workflow bot did not create a project PR."
print(f"Found project PR: products/SLFO#{project_pr_number}")
# 3. Wait for the project PR to have the label "staging/Backlog"
print(f"Checking for 'staging/Backlog' label on project PR products/SLFO#{project_pr_number}...")
backlog_label_found = False
expected_backlog_label = "staging/Backlog"
for _ in range(20):
project_pr_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
labels = project_pr_details.get("labels", [])
label_names = [l["name"] for l in labels]
if expected_backlog_label in label_names:
backlog_label_found = True
break
time.sleep(1)
assert backlog_label_found, f"Project PR products/SLFO#{project_pr_number} does not have the expected label '{expected_backlog_label}'."
print(f"Project PR products/SLFO#{project_pr_number} has the expected label '{expected_backlog_label}'.")
# 4. Post approval from autogits_obs_staging_bot
print(f"--- Posting approval from autogits_obs_staging_bot on project PR products/SLFO#{project_pr_number} ---")
staging_bot_client.create_review("products/SLFO", project_pr_number, event="APPROVED", body="Staging OK")
# 5. Check that the project PR has the label "review/Pending"
print(f"Checking for 'review/Pending' label on project PR products/SLFO#{project_pr_number}...")
pending_label_found = False
expected_pending_label = "review/Pending"
for _ in range(20):
project_pr_details = gitea_env.get_pr_details("products/SLFO", project_pr_number)
labels = project_pr_details.get("labels", [])
label_names = [l["name"] for l in labels]
print(f"Current labels: {label_names}")
if expected_pending_label in label_names:
pending_label_found = True
break
time.sleep(1)
assert pending_label_found, f"Project PR products/SLFO#{project_pr_number} does not have the expected label '{expected_pending_label}'."
print(f"Project PR products/SLFO#{project_pr_number} has the expected label '{expected_pending_label}'.")

View File

@@ -5,6 +5,43 @@ import base64
from pathlib import Path
from tests.lib.common_test_utils import GiteaAPIClient
@pytest.mark.t001
def test_001_review_requests_matching_config(automerge_env, ownerA_client):
"""
Test scenario:
1. The package PR for pkgB is opened by ownerA (who is not a maintainer of pkgB).
2. Check that review request comes to ownerB and ownerBB (package maintainers)
AND usera and userb (from workflow.config).
"""
gitea_env, test_full_repo_name, branch_name = automerge_env
# 1. Create a package PR for pool/pkgB as ownerA
diff = """diff --git a/pkgB_test_001.txt b/pkgB_test_001.txt
new file mode 100644
index 0000000..e69de29
"""
print(f"--- Creating package PR in pool/pkgB on branch {branch_name} as ownerA ---")
package_pr = ownerA_client.create_gitea_pr("pool/pkgB", diff, "Test Review Requests Config", True, base_branch=branch_name)
package_pr_number = package_pr["number"]
print(f"Created package PR pool/pkgB#{package_pr_number}")
# 2. Check that review requests came to ownerB, ownerBB, usera, and userb
print("Checking for review requests from maintainers and workflow.config...")
reviewers_requested = set()
expected_reviewers = {"ownerB", "ownerBB", "usera", "userb"}
for _ in range(30):
reviews = gitea_env.list_reviews("pool/pkgB", package_pr_number)
reviewers_requested = {r["user"]["login"] for r in reviews if r["state"] == "REQUEST_REVIEW"}
if expected_reviewers.issubset(reviewers_requested):
break
time.sleep(1)
for reviewer in expected_reviewers:
assert reviewer in reviewers_requested, f"{reviewer} was not requested for review. Requested: {reviewers_requested}"
print(f"Confirmed: {expected_reviewers} were requested for review.")
@pytest.mark.t004
@pytest.mark.xfail(reason="the bot sometimes re-requests review from autogits_obs_staging_bot despite having the approval")
def test_004_maintainer(maintainer_env, ownerA_client):
@@ -211,6 +248,7 @@ index 0000000..e69de29
@pytest.mark.t006
@pytest.mark.xfail(reason="tbd flacky in ci")
def test_006_maintainer_rejection_removes_other_requests(maintainer_env, ownerA_client, ownerBB_client):
"""
Test scenario:

View File

@@ -6,7 +6,7 @@ COPY integration/rabbitmq-config/certs/cert.pem /usr/share/pki/trust/anchors/git
RUN update-ca-certificates
# Install git and ssh
RUN zypper -n in git-core openssh-clients binutils git-lfs
RUN zypper -n in git-core openssh-clients binutils git-lfs || (tail -n 1000 /var/log/zypper.log; exit 1)
# Copy the pre-built binary into the container
COPY workflow-pr/workflow-pr /usr/local/bin/workflow-pr

View File

@@ -9,7 +9,7 @@ RUN zypper ar -f http://download.opensuse.org/repositories/devel:/Factory:/git-w
RUN zypper --gpg-auto-import-keys ref
# Install git and ssh
RUN zypper -n in git-core openssh-clients autogits-workflow-pr binutils git-lfs
RUN zypper -n in git-core openssh-clients autogits-workflow-pr binutils git-lfs || ( tail -n 1000 /var/log/zypper.log; exit 1 )
COPY integration/workflow-pr/entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +4755 /usr/local/bin/entrypoint.sh

View File

@@ -3,5 +3,6 @@
"products/SLFO#dev",
"products/SLFO#merge",
"products/SLFO#maintainer-merge",
"products/SLFO#review-required"
"products/SLFO#review-required",
"products/SLFO#label-test"
]