From 9b950afcc1b4d5c3c255ec983372345b9cd0eb5d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:26:15 +0000 Subject: [PATCH 1/8] chore(deps): update gitlab/gitlab-ee docker tag to v16 --- tests/functional/fixtures/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env index 449bc84a1..ce8680499 100644 --- a/tests/functional/fixtures/.env +++ b/tests/functional/fixtures/.env @@ -1,2 +1,2 @@ GITLAB_IMAGE=gitlab/gitlab-ee -GITLAB_TAG=15.4.0-ee.0 +GITLAB_TAG=16.8.1-ee.0 From 8201838cd92b932a33f080380d34be0923bbf5f5 Mon Sep 17 00:00:00 2001 From: Tim Knight Date: Thu, 8 Feb 2024 10:49:09 +0000 Subject: [PATCH 2/8] test: update tests for gitlab 16.8 functionality - use programmatic dates for expires_at in tokens tests - set PAT for 16.8 into tests Signed-off-by: Tim Knight --- docs/cli-examples.rst | 7 +- tests/functional/api/test_deploy_tokens.py | 8 ++- tests/functional/api/test_groups.py | 4 +- tests/functional/api/test_merge_requests.py | 4 +- tests/functional/api/test_users.py | 2 +- tests/functional/cli/test_cli_repository.py | 22 +++++- .../cli/test_cli_resource_access_tokens.py | 7 +- tests/functional/cli/test_cli_users.py | 5 ++ tests/functional/cli/test_cli_v4.py | 68 +++++++++--------- tests/functional/conftest.py | 71 +++++++++++++------ tests/functional/fixtures/set_token.rb | 2 +- tests/functional/helpers.py | 32 +++++++-- 12 files changed, 153 insertions(+), 79 deletions(-) diff --git a/docs/cli-examples.rst b/docs/cli-examples.rst index 7408d9bad..2ed1c5804 100644 --- a/docs/cli-examples.rst +++ b/docs/cli-examples.rst @@ -9,9 +9,11 @@ CLI examples CI Lint ------- +**ci-lint has been Removed in Gitlab 16, use project-ci-lint instead** + Lint a CI YAML configuration from a string: -.. note:: +.. note:: To see output, you will need to use the ``-v``/``--verbose`` flag. @@ -39,6 +41,9 @@ Validate a CI YAML configuration from a file (lints and exits with non-zero on f $ gitlab ci-lint validate --content @.gitlab-ci.yml +Project CI Lint +--------------- + Lint a project's CI YAML configuration: .. code-block:: console diff --git a/tests/functional/api/test_deploy_tokens.py b/tests/functional/api/test_deploy_tokens.py index 538dabe53..888afd7fd 100644 --- a/tests/functional/api/test_deploy_tokens.py +++ b/tests/functional/api/test_deploy_tokens.py @@ -1,9 +1,13 @@ +from datetime import date + + def test_project_deploy_tokens(gl, project): + today = date.today().isoformat() deploy_token = project.deploytokens.create( { "name": "foo", "username": "bar", - "expires_at": "2022-01-01", + "expires_at": today, "scopes": ["read_registry"], } ) @@ -12,7 +16,7 @@ def test_project_deploy_tokens(gl, project): deploy_token = project.deploytokens.get(deploy_token.id) assert deploy_token.name == "foo" - assert deploy_token.expires_at == "2022-01-01T00:00:00.000Z" + assert deploy_token.expires_at == today + "T00:00:00.000Z" assert deploy_token.scopes == ["read_registry"] assert deploy_token.username == "bar" diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py index ec381d594..a2082c0e3 100644 --- a/tests/functional/api/test_groups.py +++ b/tests/functional/api/test_groups.py @@ -10,7 +10,7 @@ def test_groups(gl): "email": "user@test.com", "username": "user", "name": "user", - "password": "user_pass", + "password": "E4596f8be406Bc3a14a4ccdb1df80587", } ) user2 = gl.users.create( @@ -18,7 +18,7 @@ def test_groups(gl): "email": "user2@test.com", "username": "user2", "name": "user2", - "password": "user2_pass", + "password": "E4596f8be406Bc3a14a4ccdb1df80587$2", } ) group1 = gl.groups.create( diff --git a/tests/functional/api/test_merge_requests.py b/tests/functional/api/test_merge_requests.py index 7557c6ac9..704fb2484 100644 --- a/tests/functional/api/test_merge_requests.py +++ b/tests/functional/api/test_merge_requests.py @@ -16,7 +16,7 @@ def test_merge_requests(project): } ) - source_branch = "branch1" + source_branch = "branch-merge-request-api" project.branches.create({"branch": source_branch, "ref": "main"}) project.files.create( @@ -28,7 +28,7 @@ def test_merge_requests(project): } ) project.mergerequests.create( - {"source_branch": "branch1", "target_branch": "main", "title": "MR readme2"} + {"source_branch": source_branch, "target_branch": "main", "title": "MR readme2"} ) diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py index 9c300365b..1a7728462 100644 --- a/tests/functional/api/test_users.py +++ b/tests/functional/api/test_users.py @@ -13,7 +13,7 @@ def test_create_user(gl, fixture_dir): "email": "foo@bar.com", "username": "foo", "name": "foo", - "password": "foo_password", + "password": "E4596f8be406Bc3a14a4ccdb1df80587$3", "avatar": open(fixture_dir / "avatar.png", "rb"), } ) diff --git a/tests/functional/cli/test_cli_repository.py b/tests/functional/cli/test_cli_repository.py index 2726d34ec..c20ee9d35 100644 --- a/tests/functional/cli/test_cli_repository.py +++ b/tests/functional/cli/test_cli_repository.py @@ -1,4 +1,5 @@ import json +import logging import time @@ -31,9 +32,13 @@ def test_list_all_commits(gitlab_cli, project): data = { "branch": "new-branch", "start_branch": "main", - "commit_message": "New commit on new branch", + "commit_message": "chore: test commit on new branch", "actions": [ - {"action": "create", "file_path": "new-file", "content": "new content"} + { + "action": "create", + "file_path": "test-cli-repo.md", + "content": "new content", + } ], } commit = project.commits.create(data) @@ -76,6 +81,19 @@ def test_commit_merge_requests(gitlab_cli, project, merge_request, wait_for_side """This tests the `project-commit merge-requests` command and also tests that we can print the result using the `json` formatter""" # Merge the MR first + wait_for_sidekiq(timeout=60) + + logging.info(f"MR status: {merge_request.state}") + logging.info(f"MR merge status: {merge_request.detailed_merge_status}") + + if merge_request.detailed_merge_status == "not_approved": + logging.info("Approving Merge Request") + + merge_request.approve() + + logging.info(f"MR merge status: {merge_request.detailed_merge_status}") + time.sleep(5) + merge_result = merge_request.merge(should_remove_source_branch=True) wait_for_sidekiq(timeout=60) diff --git a/tests/functional/cli/test_cli_resource_access_tokens.py b/tests/functional/cli/test_cli_resource_access_tokens.py index 85136b3de..16705764b 100644 --- a/tests/functional/cli/test_cli_resource_access_tokens.py +++ b/tests/functional/cli/test_cli_resource_access_tokens.py @@ -1,4 +1,4 @@ -import pytest +from datetime import date def test_list_project_access_tokens(gitlab_cli, project): @@ -18,13 +18,14 @@ def test_create_project_access_token_with_scopes(gitlab_cli, project): "test-token", "--scopes", "api,read_repository", + "--expires-at", + date.today().isoformat(), ] ret = gitlab_cli(cmd) assert ret.success -@pytest.mark.skip(reason="Requires GitLab 14.7") def test_list_group_access_tokens(gitlab_cli, group): cmd = ["group-access-token", "list", "--group-id", group.id] ret = gitlab_cli(cmd) @@ -42,6 +43,8 @@ def test_create_group_access_token_with_scopes(gitlab_cli, group): "test-token", "--scopes", "api,read_repository", + "--expires-at", + date.today().isoformat(), ] ret = gitlab_cli(cmd) diff --git a/tests/functional/cli/test_cli_users.py b/tests/functional/cli/test_cli_users.py index 8bf2fbcd4..88fe60b1b 100644 --- a/tests/functional/cli/test_cli_users.py +++ b/tests/functional/cli/test_cli_users.py @@ -1,3 +1,6 @@ +from datetime import date + + def test_create_user_impersonation_token_with_scopes(gitlab_cli, user): cmd = [ "user-impersonation-token", @@ -8,6 +11,8 @@ def test_create_user_impersonation_token_with_scopes(gitlab_cli, user): "test-token", "--scopes", "api,read_user", + "--expires-at", + date.today().isoformat(), ] ret = gitlab_cli(cmd) diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py index a5b989700..7fd7ff532 100644 --- a/tests/functional/cli/test_cli_v4.py +++ b/tests/functional/cli/test_cli_v4.py @@ -1,5 +1,9 @@ +import logging import os import time +from datetime import date + +branch = "branch-cli-v4" def test_create_project(gitlab_cli): @@ -22,28 +26,6 @@ def test_update_project(gitlab_cli, project): assert description in ret.stdout -def test_create_ci_lint(gitlab_cli, valid_gitlab_ci_yml): - cmd = ["ci-lint", "create", "--content", valid_gitlab_ci_yml] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_validate_ci_lint(gitlab_cli, valid_gitlab_ci_yml): - cmd = ["ci-lint", "validate", "--content", valid_gitlab_ci_yml] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_validate_ci_lint_invalid_exits_non_zero(gitlab_cli, invalid_gitlab_ci_yml): - cmd = ["ci-lint", "validate", "--content", invalid_gitlab_ci_yml] - ret = gitlab_cli(cmd) - - assert not ret.success - assert "CI YAML Lint failed (Invalid configuration format)" in ret.stderr - - def test_validate_project_ci_lint(gitlab_cli, project, valid_gitlab_ci_yml): cmd = [ "project-ci-lint", @@ -103,7 +85,7 @@ def test_create_user(gitlab_cli, gl): email = "fake@email.com" username = "user1" name = "User One" - password = "fakepassword" + password = "E4596f8be406Bc3a14a4ccdb1df80587" cmd = [ "user", @@ -215,8 +197,6 @@ def test_create_issue_note(gitlab_cli, issue): def test_create_branch(gitlab_cli, project): - branch = "branch1" - cmd = [ "project-branch", "create", @@ -233,7 +213,6 @@ def test_create_branch(gitlab_cli, project): def test_create_merge_request(gitlab_cli, project): - branch = "branch1" cmd = [ "project-merge-request", @@ -257,15 +236,35 @@ def test_accept_request_merge(gitlab_cli, project, wait_for_sidekiq): mr = project.mergerequests.list()[0] file_data = { "branch": mr.source_branch, - "file_path": "README2", + "file_path": "test-cli-v4.md", "content": "Content", - "commit_message": "Pre-merge commit", + "commit_message": "chore: test-cli-v4 change", } project.files.create(file_data) - time.sleep(2) + time.sleep(30) wait_for_sidekiq(timeout=60) - cmd = [ + logging.info(f"MR status: {mr.state}") + logging.info(f"MR merge status: {mr.detailed_merge_status}") + + if mr.detailed_merge_status == "not_approved": + logging.info("Approving Merge Request") + + approve_cmd = [ + "project-merge-request", + "approve", + "--project-id", + project.id, + "--iid", + mr.iid, + ] + gitlab_cli(approve_cmd) + + time.sleep(5) + logging.info(f"MR merge status: {mr.detailed_merge_status}") + + time.sleep(0.5) + approve_cmd = [ "project-merge-request", "merge", "--project-id", @@ -273,7 +272,7 @@ def test_accept_request_merge(gitlab_cli, project, wait_for_sidekiq): "--iid", mr.iid, ] - ret = gitlab_cli(cmd) + ret = gitlab_cli(approve_cmd) assert ret.success @@ -501,9 +500,6 @@ def test_delete_project_variable(gitlab_cli, variable): def test_delete_branch(gitlab_cli, project): - # TODO: branch fixture - branch = "branch1" - cmd = ["project-branch", "delete", "--project-id", project.id, "--name", branch] ret = gitlab_cli(cmd) @@ -590,7 +586,7 @@ def test_create_project_with_values_at_prefixed(gitlab_cli, tmpdir): def test_create_project_deploy_token(gitlab_cli, project): name = "project-token" username = "root" - expires_at = "2021-09-09" + expires_at = date.today().isoformat() scopes = "read_registry" cmd = [ @@ -666,7 +662,7 @@ def test_delete_project_deploy_token(gitlab_cli, deploy_token): def test_create_group_deploy_token(gitlab_cli, group): name = "group-token" username = "root" - expires_at = "2021-09-09" + expires_at = date.today().isoformat() scopes = "read_registry" cmd = [ diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index 34b286b4d..62304a411 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -4,6 +4,7 @@ import tempfile import time import uuid +from datetime import date from subprocess import check_output from typing import Optional @@ -70,17 +71,15 @@ def reset_gitlab(gl: gitlab.Gitlab) -> None: exist.""" if helpers.get_gitlab_plan(gl): logging.info("GitLab EE detected") - # NOTE(jlvillal): By default in GitLab EE it will wait 7 days before - # deleting a group. Disable delayed group/project deletion. + # NOTE(jlvillal, timknight): By default in GitLab EE it will wait 7 days before + # deleting a group or project. + # In GL 16.0 we need to call delete with `permanently_remove=True` for projects and sub groups + # (handled in helpers.py safe_delete) settings = gl.settings.get() modified_settings = False - if settings.delayed_group_deletion: - logging.info("Setting `delayed_group_deletion` to False") - settings.delayed_group_deletion = False - modified_settings = True - if settings.delayed_project_deletion: - logging.info("Setting `delayed_project_deletion` to False") - settings.delayed_project_deletion = False + if settings.deletion_adjourned_period != 1: + logging.info("Setting `deletion_adjourned_period` to 1 Day") + settings.deletion_adjourned_period = 1 modified_settings = True if modified_settings: settings.save() @@ -122,7 +121,7 @@ def reset_gitlab(gl: gitlab.Gitlab) -> None: for user in gl.users.list(): if user.username not in ["root", "ghost"]: logging.info(f"Deleting user: {user.username!r}") - helpers.safe_delete(user, hard_delete=True) + helpers.safe_delete(user) def set_token(container: str, fixture_dir: pathlib.Path) -> str: @@ -212,21 +211,32 @@ def _check( @pytest.fixture def wait_for_sidekiq(gl): """ - Return a helper function to wait until there are no busy sidekiq processes. + Return a helper function to wait until there are spare sidekiq processes. + + Because not all Groups can be deleted immediately in GL 16, we will likely have busy Sidekiq jobs + set aside for their deletion in 1 days time. Use this with asserts for slow tasks (group/project/user creation/deletion). + + { ..., 'labels': [], 'concurrency': 20, 'busy': 3} """ def _wait(timeout: int = 30, step: float = 0.5, allow_fail: bool = False) -> bool: for count in range(timeout): time.sleep(step) - busy = False processes = gl.sidekiq.process_metrics()["processes"] + busy_processes = 0 + total_concurrency = 0 for process in processes: - if process["busy"]: - busy = True - if not busy: + busy_processes += process["busy"] + total_concurrency += process["concurrency"] + + logging.info(f"Busy processes {busy_processes}") + # If we have space concurrency in the process, continue + if busy_processes < total_concurrency: + logging.info("Spare sidekiq process found") return True + logging.info(f"sidekiq busy {count} of {timeout}") assert allow_fail, "sidekiq process should have terminated but did not." return False @@ -400,6 +410,7 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): assert result is True, "sidekiq process should have terminated but did not" project.refresh() # Gets us the current default branch + logging.info(f"Creating branch {source_branch}") mr_branch = project.branches.create( {"branch": source_branch, "ref": project.default_branch} ) @@ -413,6 +424,11 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): "commit_message": "New commit in new branch", } ) + + # Helps with Debugging why MRs fail to merge resulting in 405 from downstream tests + approval_rules = project.approvalrules.list() + logging.info(f"Project MR Approval Rules {approval_rules}") + if create_pipeline: project.files.create( { @@ -428,6 +444,7 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): "commit_message": "Add a simple pipeline", } ) + logging.info(f"Creating merge request for {source_branch}") mr = project.mergerequests.create( { "source_branch": source_branch, @@ -442,10 +459,19 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): mr_iid = mr.iid for _ in range(60): mr = project.mergerequests.get(mr_iid) - if mr.merge_status != "checking": + logging.info( + f"Waiting for Gitlab to update MR status: {mr.detailed_merge_status}" + ) + if ( + mr.detailed_merge_status == "checking" + or mr.detailed_merge_status == "unchecked" + ): + time.sleep(0.5) + else: break - time.sleep(0.5) - assert mr.merge_status != "checking" + + assert mr.detailed_merge_status != "checking" + assert mr.detailed_merge_status != "unchecked" to_delete.extend([mr, mr_branch]) return mr @@ -523,14 +549,13 @@ def user(gl): email = f"user{_id}@email.com" username = f"user{_id}" name = f"User {_id}" - password = "fakepassword" + password = "E4596f8be406Bc3a14a4ccdb1df80587" user = gl.users.create(email=email, username=username, name=name, password=password) yield user - # Use `hard_delete=True` or a 'Ghost User' may be created. - helpers.safe_delete(user, hard_delete=True) + helpers.safe_delete(user) @pytest.fixture(scope="module") @@ -599,7 +624,7 @@ def deploy_token(project): data = { "name": f"token-{_id}", "username": "root", - "expires_at": "2021-09-09", + "expires_at": date.today().isoformat(), "scopes": "read_registry", } @@ -613,7 +638,7 @@ def group_deploy_token(group): data = { "name": f"group-token-{_id}", "username": "root", - "expires_at": "2021-09-09", + "expires_at": date.today().isoformat(), "scopes": "read_registry", } diff --git a/tests/functional/fixtures/set_token.rb b/tests/functional/fixtures/set_token.rb index 503588b9c..26d4c0a2a 100644 --- a/tests/functional/fixtures/set_token.rb +++ b/tests/functional/fixtures/set_token.rb @@ -2,7 +2,7 @@ user = User.find_by_username('root') -token = user.personal_access_tokens.first_or_create(scopes: [:api, :sudo], name: 'default'); +token = user.personal_access_tokens.first_or_create(scopes: ['api', 'sudo'], name: 'default', expires_at: 365.days.from_now); token.set_token('python-gitlab-token'); token.save! diff --git a/tests/functional/helpers.py b/tests/functional/helpers.py index 2b95f3abf..c9dae1619 100644 --- a/tests/functional/helpers.py +++ b/tests/functional/helpers.py @@ -26,11 +26,7 @@ def get_gitlab_plan(gl: gitlab.Gitlab) -> Optional[str]: return license["plan"] -def safe_delete( - object: gitlab.base.RESTObject, - *, - hard_delete: bool = False, -) -> None: +def safe_delete(object: gitlab.base.RESTObject) -> None: """Ensure the object specified can not be retrieved. If object still exists after timeout period, fail the test""" manager = object.manager @@ -40,15 +36,37 @@ def safe_delete( except gitlab.exceptions.GitlabGetError: return + logging.info(f"Deleting object type {type(object)}") + if index: logging.info(f"Attempt {index + 1} to delete {object!r}.") try: - if hard_delete: + if type(object) is gitlab.v4.objects.User: + # You can't use this option if the selected user is the sole owner of any groups + # Use `hard_delete=True` or a 'Ghost User' may be created. + # https://docs.gitlab.com/ee/api/users.html#user-deletion object.delete(hard_delete=True) + if index > 1: + # If User is the sole owner of any group it won't be deleted, + # which combined with parents group never immediately deleting in GL 16 + # we shouldn't cause test to fail if it still exists + return + elif type(object) is gitlab.v4.objects.Project: + # Immediately delete rather than waiting for at least 1day + # https://docs.gitlab.com/ee/api/projects.html#delete-project + object.delete(permanently_remove=True) + pass else: + # We only attempt to delete parent groups to prevent dangling sub-groups + # However parent groups can only be deleted on a delay in Gl 16 + # https://docs.gitlab.com/ee/api/projects.html#delete-group object.delete() except gitlab.exceptions.GitlabDeleteError: - logging.info(f"{object!r} already deleted.") + logging.info(f"{object!r} already deleted or scheduled for deletion.") + if type(object) is gitlab.v4.objects.Group: + # Parent groups can never be immediately deleted in GL 16, + # so don't cause test to fail if it still exists + return pass time.sleep(SLEEP_INTERVAL) From c0c70ce5a589cfa2544ac11c7fce2797092b2acf Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Mon, 13 Feb 2023 15:45:43 +0000 Subject: [PATCH 3/8] test: don't use weak passwords Newer versions of GitLab will refuse to create a user with a weak password. In order for us to move to a newer GitLab version in testing use a stronger password for the tests that create a user. --- tests/functional/api/test_groups.py | 4 ++-- tests/functional/api/test_users.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py index a2082c0e3..14e6a06c9 100644 --- a/tests/functional/api/test_groups.py +++ b/tests/functional/api/test_groups.py @@ -10,7 +10,7 @@ def test_groups(gl): "email": "user@test.com", "username": "user", "name": "user", - "password": "E4596f8be406Bc3a14a4ccdb1df80587", + "password": "E4596f8be406Bc3a14a4ccdb1df80587#!1", } ) user2 = gl.users.create( @@ -18,7 +18,7 @@ def test_groups(gl): "email": "user2@test.com", "username": "user2", "name": "user2", - "password": "E4596f8be406Bc3a14a4ccdb1df80587$2", + "password": "E4596f8be406Bc3a14a4ccdb1df80587#!#2", } ) group1 = gl.groups.create( diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py index 1a7728462..17a4aa18b 100644 --- a/tests/functional/api/test_users.py +++ b/tests/functional/api/test_users.py @@ -66,7 +66,7 @@ def test_delete_user(gl, wait_for_sidekiq): "email": "delete-user@test.com", "username": "delete-user", "name": "delete-user", - "password": "delete-user-pass", + "password": "E4596f8be406Bc3a14a4ccdb1df80587#!", } ) @@ -101,7 +101,7 @@ def test_list_multiple_users(gl, user): "email": second_email, "username": second_username, "name": "Foo Bar", - "password": "foobar_password", + "password": "E4596f8be406Bc3a14a4ccdb1df80587#!", } ) assert gl.users.list(search=second_user.username)[0].id == second_user.id From 63d9dd8197f8743b3b34237ea128e33864ed5fee Mon Sep 17 00:00:00 2001 From: Nejc Habjan Date: Tue, 29 Aug 2023 01:09:50 +0200 Subject: [PATCH 4/8] test(functional): enable bulk import feature flag before test --- tests/functional/api/test_bulk_imports.py | 27 ++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/functional/api/test_bulk_imports.py b/tests/functional/api/test_bulk_imports.py index 899d35840..6eda12ab9 100644 --- a/tests/functional/api/test_bulk_imports.py +++ b/tests/functional/api/test_bulk_imports.py @@ -1,4 +1,29 @@ -def test_bulk_imports(gl, group): +import time +import pytest + +import gitlab + + +@pytest.fixture +def bulk_import_enabled(gl: gitlab.Gitlab): + settings = gl.settings.get() + bulk_import_default = settings.bulk_import_enabled + + settings.bulk_import_enabled = True + settings.save() + + # todo: why so fussy with feature flag timing? + time.sleep(5) + get_settings = gl.settings.get() + assert get_settings.bulk_import_enabled == True + + yield settings + + settings.bulk_import_enabled = bulk_import_default + settings.save() + + +def test_bulk_imports(gl, group, bulk_import_enabled): destination = f"{group.full_path}-import" configuration = { "url": gl.url, From 3b3b7c20e51b3288cb2e4193628538c575ae9c19 Mon Sep 17 00:00:00 2001 From: Tim Knight Date: Mon, 12 Feb 2024 11:26:31 +0000 Subject: [PATCH 5/8] test: update api tests for GL 16.10 - Make sure we're testing python-gitlab functionality, make sure we're not awaiting on Gitlab Async functions - Decouple and improve test stability Signed-off-by: Tim Knight --- tests/functional/api/test_boards.py | 2 - tests/functional/api/test_bulk_imports.py | 5 +- tests/functional/api/test_current_user.py | 4 +- tests/functional/api/test_deploy_keys.py | 2 +- tests/functional/api/test_deploy_tokens.py | 8 +-- tests/functional/api/test_epics.py | 1 - tests/functional/api/test_gitlab.py | 3 - tests/functional/api/test_groups.py | 8 +-- tests/functional/api/test_import_export.py | 9 +++ tests/functional/api/test_issues.py | 9 +-- tests/functional/api/test_lazy_objects.py | 7 ++- tests/functional/api/test_merge_requests.py | 58 +++++++++--------- tests/functional/api/test_projects.py | 27 +++------ tests/functional/api/test_releases.py | 1 - tests/functional/api/test_repository.py | 4 +- tests/functional/api/test_services.py | 4 +- tests/functional/api/test_snippets.py | 18 ++++-- tests/functional/api/test_topics.py | 1 - tests/functional/api/test_users.py | 54 +++++++++-------- tests/functional/api/test_variables.py | 3 - tests/functional/cli/test_cli_repository.py | 27 +++------ .../cli/test_cli_resource_access_tokens.py | 6 +- tests/functional/cli/test_cli_users.py | 4 +- tests/functional/cli/test_cli_v4.py | 19 ++---- tests/functional/conftest.py | 60 ++++--------------- tests/functional/fixtures/.env | 2 +- tests/functional/helpers.py | 10 ++-- 27 files changed, 140 insertions(+), 216 deletions(-) diff --git a/tests/functional/api/test_boards.py b/tests/functional/api/test_boards.py index 24c62ca67..1679a14e9 100644 --- a/tests/functional/api/test_boards.py +++ b/tests/functional/api/test_boards.py @@ -5,7 +5,6 @@ def test_project_boards(project): board = project.boards.get(board.id) project.boards.delete(board.id) - assert not project.boards.list() def test_group_boards(group): @@ -15,4 +14,3 @@ def test_group_boards(group): board = group.boards.get(board.id) group.boards.delete(board.id) - assert not group.boards.list() diff --git a/tests/functional/api/test_bulk_imports.py b/tests/functional/api/test_bulk_imports.py index 6eda12ab9..a9a649fcc 100644 --- a/tests/functional/api/test_bulk_imports.py +++ b/tests/functional/api/test_bulk_imports.py @@ -1,4 +1,5 @@ import time + import pytest import gitlab @@ -15,7 +16,7 @@ def bulk_import_enabled(gl: gitlab.Gitlab): # todo: why so fussy with feature flag timing? time.sleep(5) get_settings = gl.settings.get() - assert get_settings.bulk_import_enabled == True + assert get_settings.bulk_import_enabled is True yield settings @@ -23,6 +24,8 @@ def bulk_import_enabled(gl: gitlab.Gitlab): settings.save() +# https://github.com/python-gitlab/python-gitlab/pull/2790#pullrequestreview-1873617123 +@pytest.mark.xfail(reason="Bulk Imports to be worked on in a follow up") def test_bulk_imports(gl, group, bulk_import_enabled): destination = f"{group.full_path}-import" configuration = { diff --git a/tests/functional/api/test_current_user.py b/tests/functional/api/test_current_user.py index b12145e48..561dbe4b0 100644 --- a/tests/functional/api/test_current_user.py +++ b/tests/functional/api/test_current_user.py @@ -4,7 +4,6 @@ def test_current_user_email(gl): assert mail in gl.user.emails.list() mail.delete() - assert mail not in gl.user.emails.list() def test_current_user_gpg_keys(gl, GPG_KEY): @@ -14,8 +13,8 @@ def test_current_user_gpg_keys(gl, GPG_KEY): # Seems broken on the gitlab side gkey = gl.user.gpgkeys.get(gkey.id) + gkey.delete() - assert gkey not in gl.user.gpgkeys.list() def test_current_user_ssh_keys(gl, SSH_KEY): @@ -24,7 +23,6 @@ def test_current_user_ssh_keys(gl, SSH_KEY): assert key in gl.user.keys.list() key.delete() - assert key not in gl.user.keys.list() def test_current_user_status(gl): diff --git a/tests/functional/api/test_deploy_keys.py b/tests/functional/api/test_deploy_keys.py index 1fbaa18e6..ac65555cc 100644 --- a/tests/functional/api/test_deploy_keys.py +++ b/tests/functional/api/test_deploy_keys.py @@ -7,5 +7,5 @@ def test_project_deploy_keys(gl, project, DEPLOY_KEY): assert deploy_key in project2.keys.list() project2.keys.delete(deploy_key.id) - assert deploy_key not in project2.keys.list() + project2.delete() diff --git a/tests/functional/api/test_deploy_tokens.py b/tests/functional/api/test_deploy_tokens.py index 888afd7fd..3dcaf127e 100644 --- a/tests/functional/api/test_deploy_tokens.py +++ b/tests/functional/api/test_deploy_tokens.py @@ -1,8 +1,8 @@ -from datetime import date +import datetime def test_project_deploy_tokens(gl, project): - today = date.today().isoformat() + today = datetime.date.today().isoformat() deploy_token = project.deploytokens.create( { "name": "foo", @@ -21,8 +21,6 @@ def test_project_deploy_tokens(gl, project): assert deploy_token.username == "bar" deploy_token.delete() - assert deploy_token not in project.deploytokens.list() - assert deploy_token not in gl.deploytokens.list() def test_group_deploy_tokens(gl, group): @@ -41,5 +39,3 @@ def test_group_deploy_tokens(gl, group): assert deploy_token.scopes == ["read_registry"] deploy_token.delete() - assert deploy_token not in group.deploytokens.list() - assert deploy_token not in gl.deploytokens.list() diff --git a/tests/functional/api/test_epics.py b/tests/functional/api/test_epics.py index 073ca2ad7..a4f6765da 100644 --- a/tests/functional/api/test_epics.py +++ b/tests/functional/api/test_epics.py @@ -23,7 +23,6 @@ def test_epic_issues(epic, issue): assert epic.issues.list() epic_issue.delete() - assert not epic.issues.list() def test_epic_notes(epic): diff --git a/tests/functional/api/test_gitlab.py b/tests/functional/api/test_gitlab.py index 4668fa71e..af505c73b 100644 --- a/tests/functional/api/test_gitlab.py +++ b/tests/functional/api/test_gitlab.py @@ -53,7 +53,6 @@ def test_broadcast_messages(gl, get_all_kwargs): assert msg.color == "#444444" msg.delete() - assert msg not in gl.broadcastmessages.list() def test_markdown(gl): @@ -151,7 +150,6 @@ def test_hooks(gl): assert hook in gl.hooks.list() hook.delete() - assert hook not in gl.hooks.list() def test_namespaces(gl, get_all_kwargs): @@ -202,7 +200,6 @@ def test_features(gl): assert feat in gl.features.list() feat.delete() - assert feat not in gl.features.list() def test_pagination(gl, project): diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py index 14e6a06c9..8efc3245d 100644 --- a/tests/functional/api/test_groups.py +++ b/tests/functional/api/test_groups.py @@ -105,8 +105,9 @@ def test_groups(gl): assert result[0].id == user.id group1.members.delete(user.id) - assert user not in group1.members.list() + assert group1.members_all.list() + member = group1.members.get(user2.id) member.access_level = gitlab.const.AccessLevel.OWNER member.save() @@ -135,7 +136,6 @@ def test_group_labels(group): assert label.name == "Label:that requires:encoding" label.delete() - assert label not in group.labels.list() @pytest.mark.gitlab_premium @@ -194,7 +194,6 @@ def test_group_badges(group): assert badge.image_url == "http://another.example.com" badge.delete() - assert badge not in group.badges.list() def test_group_milestones(group): @@ -228,7 +227,6 @@ def test_group_custom_attributes(gl, group): assert attr in group.customattributes.list() attr.delete() - assert attr not in group.customattributes.list() def test_group_subgroups_projects(gl, user): @@ -270,7 +268,6 @@ def test_group_wiki(group): wiki.save() wiki.delete() - assert wiki not in group.wikis.list() @pytest.mark.gitlab_premium @@ -285,7 +282,6 @@ def test_group_hooks(group): assert hook.note_events is True hook.delete() - assert hook not in group.hooks.list() def test_group_transfer(gl, group): diff --git a/tests/functional/api/test_import_export.py b/tests/functional/api/test_import_export.py index 8f9db9c60..6f70a810a 100644 --- a/tests/functional/api/test_import_export.py +++ b/tests/functional/api/test_import_export.py @@ -5,6 +5,7 @@ import gitlab +# https://github.com/python-gitlab/python-gitlab/pull/2790#pullrequestreview-1873617123 def test_group_import_export(gl, group, temp_dir): export = group.exports.create() assert export.message == "202 Accepted" @@ -31,6 +32,8 @@ def test_group_import_export(gl, group, temp_dir): assert group_import.name == import_name +# https://github.com/python-gitlab/python-gitlab/pull/2790#pullrequestreview-1873617123 +@pytest.mark.xfail(reason="test_project_import_export to be worked on in a follow up") def test_project_import_export(gl, project, temp_dir): export = project.exports.create() assert export.message == "202 Accepted" @@ -68,6 +71,8 @@ def test_project_import_export(gl, project, temp_dir): raise Exception("Project import taking too much time") +# https://github.com/python-gitlab/python-gitlab/pull/2790#pullrequestreview-1873617123 +@pytest.mark.xfail(reason="test_project_remote_import to be worked on in a follow up") def test_project_remote_import(gl): with pytest.raises(gitlab.exceptions.GitlabImportError) as err_info: gl.projects.remote_import( @@ -80,6 +85,10 @@ def test_project_remote_import(gl): ) +# https://github.com/python-gitlab/python-gitlab/pull/2790#pullrequestreview-1873617123 +@pytest.mark.xfail( + reason="test_project_remote_import_s3 to be worked on in a follow up" +) def test_project_remote_import_s3(gl): gl.features.set("import_project_from_remote_file_s3", True) with pytest.raises(gitlab.exceptions.GitlabImportError) as err_info: diff --git a/tests/functional/api/test_issues.py b/tests/functional/api/test_issues.py index 1a373bd96..98825d027 100644 --- a/tests/functional/api/test_issues.py +++ b/tests/functional/api/test_issues.py @@ -18,8 +18,6 @@ def test_create_issue(project): assert issue in project.issues.list(state="opened") assert issue2 in project.issues.list(state="closed") - assert isinstance(issue.user_agent_detail(), dict) - assert issue.user_agent_detail()["user_agent"] assert issue.participants() assert type(issue.closed_by()) == list assert type(issue.related_merge_requests()) == list @@ -33,10 +31,7 @@ def test_issue_notes(issue): assert emoji in note.awardemojis.list() emoji.delete() - assert emoji not in note.awardemojis.list() - note.delete() - assert note not in issue.notes.list() def test_issue_labels(project, issue): @@ -62,8 +57,8 @@ def test_issue_links(project, issue): assert links link_id = links[0].issue_link_id + issue.links.delete(link_id) - assert not issue.links.list() def test_issue_label_events(issue): @@ -114,5 +109,3 @@ def test_issue_discussions(issue): assert discussion.attributes["notes"][-1]["body"] == "updated body" d_note_from_get.delete() - discussion = issue.discussions.get(discussion.id) - assert len(discussion.attributes["notes"]) == 1 diff --git a/tests/functional/api/test_lazy_objects.py b/tests/functional/api/test_lazy_objects.py index cd149b422..607a63648 100644 --- a/tests/functional/api/test_lazy_objects.py +++ b/tests/functional/api/test_lazy_objects.py @@ -1,3 +1,5 @@ +import time + import pytest import gitlab @@ -27,9 +29,10 @@ def test_save_after_lazy_get_with_path(project, lazy_project): assert lazy_project.description == "A new description" -def test_delete_after_lazy_get_with_path(gl, group, wait_for_sidekiq): +def test_delete_after_lazy_get_with_path(gl, group): project = gl.projects.create({"name": "lazy_project", "namespace_id": group.id}) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) lazy_project = gl.projects.get(project.path_with_namespace, lazy=True) lazy_project.delete() diff --git a/tests/functional/api/test_merge_requests.py b/tests/functional/api/test_merge_requests.py index 704fb2484..fe54abd94 100644 --- a/tests/functional/api/test_merge_requests.py +++ b/tests/functional/api/test_merge_requests.py @@ -1,3 +1,4 @@ +import datetime import time import pytest @@ -72,8 +73,6 @@ def test_merge_request_discussion(project): assert discussion.attributes["notes"][-1]["body"] == "updated body" note_from_get.delete() - discussion = mr.discussions.get(discussion.id) - assert len(discussion.attributes["notes"]) == 1 def test_merge_request_labels(project): @@ -164,27 +163,33 @@ def test_project_merge_request_approval_rules(group, project): assert approval_rules[0].approvals_required == 2 approval_rules[0].delete() - ars = project.approvalrules.list(get_all=True) - assert len(ars) == 0 -def test_merge_request_reset_approvals(gitlab_url, project, wait_for_sidekiq): - bot = project.access_tokens.create({"name": "bot", "scopes": ["api"]}) +def test_merge_request_reset_approvals(gitlab_url, project): + today = datetime.date.today() + future_date = today + datetime.timedelta(days=4) + bot = project.access_tokens.create( + {"name": "bot", "scopes": ["api"], "expires_at": future_date.isoformat()} + ) + bot_gitlab = gitlab.Gitlab(gitlab_url, private_token=bot.token) bot_project = bot_gitlab.projects.get(project.id, lazy=True) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) + mr = bot_project.mergerequests.list()[0] # type: ignore[index] + assert mr.reset_approvals() -def test_cancel_merge_when_pipeline_succeeds( - project, merge_request_with_pipeline, wait_for_sidekiq -): - wait_for_sidekiq(timeout=60) +def test_cancel_merge_when_pipeline_succeeds(project, merge_request_with_pipeline): + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Set to merge when the pipeline succeeds, which should never happen merge_request_with_pipeline.merge(merge_when_pipeline_succeeds=True) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) mr = project.mergerequests.get(merge_request_with_pipeline.iid) assert mr.merged_at is None @@ -193,9 +198,10 @@ def test_cancel_merge_when_pipeline_succeeds( assert cancel == {"status": "success"} -def test_merge_request_merge(project, merge_request, wait_for_sidekiq): +def test_merge_request_merge(project, merge_request): merge_request.merge() - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) mr = project.mergerequests.get(merge_request.iid) assert mr.merged_at is not None @@ -205,15 +211,14 @@ def test_merge_request_merge(project, merge_request, wait_for_sidekiq): mr.merge() -def test_merge_request_should_remove_source_branch( - project, merge_request, wait_for_sidekiq -) -> None: +def test_merge_request_should_remove_source_branch(project, merge_request) -> None: """Test to ensure https://github.com/python-gitlab/python-gitlab/issues/1120 is fixed. Bug reported that they could not use 'should_remove_source_branch' in mr.merge() call""" merge_request.merge(should_remove_source_branch=True) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Wait until it is merged mr = None @@ -227,7 +232,8 @@ def test_merge_request_should_remove_source_branch( assert mr is not None assert mr.merged_at is not None time.sleep(0.5) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Ensure we can NOT get the MR branch with pytest.raises(gitlab.exceptions.GitlabGetError): @@ -240,9 +246,7 @@ def test_merge_request_should_remove_source_branch( print("result:", pprint.pformat(result)) -def test_merge_request_large_commit_message( - project, merge_request, wait_for_sidekiq -) -> None: +def test_merge_request_large_commit_message(project, merge_request) -> None: """Test to ensure https://github.com/python-gitlab/python-gitlab/issues/1452 is fixed. Bug reported that very long 'merge_commit_message' in mr.merge() would @@ -255,7 +259,8 @@ def test_merge_request_large_commit_message( merge_commit_message=merge_commit_message, should_remove_source_branch=False ) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Wait until it is merged mr = None @@ -279,9 +284,7 @@ def test_merge_request_merge_ref(merge_request) -> None: assert response and "commit_id" in response -def test_merge_request_merge_ref_should_fail( - project, merge_request, wait_for_sidekiq -) -> None: +def test_merge_request_merge_ref_should_fail(project, merge_request) -> None: # Create conflict project.files.create( { @@ -291,7 +294,8 @@ def test_merge_request_merge_ref_should_fail( "commit_message": "Another commit in main branch", } ) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Check for non-existing merge_ref for MR with conflicts with pytest.raises(gitlab.exceptions.GitlabGetError): diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py index 77bb8348b..75d402a0d 100644 --- a/tests/functional/api/test_projects.py +++ b/tests/functional/api/test_projects.py @@ -1,3 +1,4 @@ +import time import uuid import pytest @@ -45,7 +46,6 @@ def test_project_members(user, project): assert member.access_level == 30 member.delete() - assert member not in project.members.list() def test_project_badges(project): @@ -62,7 +62,6 @@ def test_project_badges(project): assert badge.image_url == "http://another.example.com" badge.delete() - assert badge not in project.badges.list() @pytest.mark.skip(reason="Commented out in legacy test") @@ -78,7 +77,6 @@ def test_project_boards(project): last_list.save() last_list.delete() - assert last_list not in board.lists.list() def test_project_custom_attributes(gl, project): @@ -97,7 +95,6 @@ def test_project_custom_attributes(gl, project): assert attr in project.customattributes.list() attr.delete() - assert attr not in project.customattributes.list() def test_project_environments(project): @@ -115,8 +112,8 @@ def test_project_environments(project): assert environment.external_url == "http://new.env/whatever" environment.stop() + environment.delete() - assert environment not in project.environments.list() def test_project_events(project): @@ -156,7 +153,6 @@ def test_project_hooks(project): assert hook.note_events is True hook.delete() - assert hook not in project.hooks.list() def test_project_housekeeping(project): @@ -184,7 +180,6 @@ def test_project_labels(project): assert label.subscribed is False label.delete() - assert label not in project.labels.list() def test_project_label_promotion(gl, group): @@ -206,7 +201,6 @@ def test_project_label_promotion(gl, group): assert any(label.name == label_name for label in group.labels.list()) group.labels.delete(label_name) - assert not any(label.name == label_name for label in group.labels.list()) def test_project_milestones(project): @@ -255,10 +249,9 @@ def test_project_pages_domains(gl, project): assert domain.domain == "foo.domain.com" domain.delete() - assert domain not in project.pagesdomains.list() -def test_project_protected_branches(project, wait_for_sidekiq, gitlab_version): +def test_project_protected_branches(project, gitlab_version): # Updating a protected branch is possible from Gitlab 15.6 # https://docs.gitlab.com/ee/api/protected_branches.html#update-a-protected-branch can_update_prot_branch = gitlab_version.major > 15 or ( @@ -278,13 +271,14 @@ def test_project_protected_branches(project, wait_for_sidekiq, gitlab_version): if can_update_prot_branch: p_b.allow_force_push = True p_b.save() - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) p_b = project.protectedbranches.get("*-stable") if can_update_prot_branch: assert p_b.allow_force_push - p_b.delete() - assert p_b not in project.protectedbranches.list() + + p_b.delete() def test_project_remote_mirrors(project): @@ -319,9 +313,6 @@ def test_project_services(project): service.delete() - service = project.services.get("asana") - assert service.active is False - def test_project_stars(project): project.star() @@ -342,7 +333,6 @@ def test_project_tags(project, project_file): assert tag in project.tags.list() tag.delete() - assert tag not in project.tags.list() def test_project_triggers(project): @@ -350,7 +340,6 @@ def test_project_triggers(project): assert trigger in project.triggers.list() trigger.delete() - assert trigger not in project.triggers.list() def test_project_wiki(project): @@ -364,8 +353,8 @@ def test_project_wiki(project): # update and delete seem broken wiki.content = "new content" wiki.save() + wiki.delete() - assert wiki not in project.wikis.list() def test_project_groups_list(gl, group): diff --git a/tests/functional/api/test_releases.py b/tests/functional/api/test_releases.py index c52e396c1..33b059c04 100644 --- a/tests/functional/api/test_releases.py +++ b/tests/functional/api/test_releases.py @@ -52,7 +52,6 @@ def test_update_save_project_release(project, release): def test_delete_project_release(project, release): project.releases.delete(release.tag_name) - assert release not in project.releases.list() def test_create_project_release_links(project, release): diff --git a/tests/functional/api/test_repository.py b/tests/functional/api/test_repository.py index dd70f10b1..03cca583b 100644 --- a/tests/functional/api/test_repository.py +++ b/tests/functional/api/test_repository.py @@ -158,10 +158,8 @@ def test_commit_discussion(project): note_from_get.body = "updated body" note_from_get.save() discussion = commit.discussions.get(discussion.id) - # assert discussion.attributes["notes"][-1]["body"] == "updated body" + note_from_get.delete() - discussion = commit.discussions.get(discussion.id) - # assert len(discussion.attributes["notes"]) == 1 def test_revert_commit(project): diff --git a/tests/functional/api/test_services.py b/tests/functional/api/test_services.py index 51805ef37..ce9503080 100644 --- a/tests/functional/api/test_services.py +++ b/tests/functional/api/test_services.py @@ -32,7 +32,5 @@ def test_get_service(project, service): def test_delete_service(project, service): service_object = project.services.get(service["slug"]) - service_object.delete() - service_object = project.services.get(service["slug"]) - assert not service_object.active + service_object.delete() diff --git a/tests/functional/api/test_snippets.py b/tests/functional/api/test_snippets.py index b6b1f0123..b0ea54d1d 100644 --- a/tests/functional/api/test_snippets.py +++ b/tests/functional/api/test_snippets.py @@ -1,3 +1,5 @@ +import pytest + import gitlab @@ -20,10 +22,8 @@ def test_snippets(gl): content = snippet.content() assert content.decode() == "import gitlab" - assert snippet.user_agent_detail()["user_agent"] snippet.delete() - assert snippet not in gl.snippets.list(get_all=True) def test_project_snippets(project): @@ -38,7 +38,16 @@ def test_project_snippets(project): } ) - assert snippet.user_agent_detail()["user_agent"] + assert snippet.title == "snip1" + + +@pytest.mark.xfail(reason="Returning 404 UserAgentDetail not found in GL 16") +def test_project_snippet_user_agent_detail(project): + snippet = project.snippets.list()[0] + + user_agent_detail = snippet.user_agent_detail() + + assert user_agent_detail["user_agent"] def test_project_snippet_discussion(project): @@ -56,8 +65,6 @@ def test_project_snippet_discussion(project): assert discussion.attributes["notes"][-1]["body"] == "updated body" note_from_get.delete() - discussion = snippet.discussions.get(discussion.id) - assert len(discussion.attributes["notes"]) == 1 def test_project_snippet_file(project): @@ -71,4 +78,3 @@ def test_project_snippet_file(project): assert snippet in project.snippets.list() snippet.delete() - assert snippet not in project.snippets.list() diff --git a/tests/functional/api/test_topics.py b/tests/functional/api/test_topics.py index 7777725a9..1fb7c8d63 100644 --- a/tests/functional/api/test_topics.py +++ b/tests/functional/api/test_topics.py @@ -31,4 +31,3 @@ def test_topics(gl, gitlab_version): assert merged_topic["id"] == topic2.id topic2.delete() - assert not gl.topics.list() diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py index 17a4aa18b..58c90c646 100644 --- a/tests/functional/api/test_users.py +++ b/tests/functional/api/test_users.py @@ -4,6 +4,9 @@ https://docs.gitlab.com/ee/api/users.html#delete-authentication-identity-from-user """ +import datetime +import time + import requests @@ -60,20 +63,20 @@ def test_ban_user(gl, user): assert retrieved_user.state == "active" -def test_delete_user(gl, wait_for_sidekiq): +def test_delete_user(gl): new_user = gl.users.create( { "email": "delete-user@test.com", "username": "delete-user", "name": "delete-user", - "password": "E4596f8be406Bc3a14a4ccdb1df80587#!", + "password": "E4596f8be406Bc3a14a4ccdb1df80587#15", } ) - new_user.delete() - wait_for_sidekiq(timeout=60) + # We don't need to validate Gitlab's behaviour by checking if user is present after a delay etc, + # just that python-gitlab acted correctly to produce a 2xx from Gitlab - assert new_user.id not in [user.id for user in gl.users.list()] + new_user.delete() def test_user_projects_list(gl, user): @@ -117,11 +120,7 @@ def test_user_gpg_keys(gl, user, GPG_KEY): gkey = user.gpgkeys.create({"key": GPG_KEY}) assert gkey in user.gpgkeys.list() - # Seems broken on the gitlab side - # gkey = user.gpgkeys.get(gkey.id) - gkey.delete() - assert gkey not in user.gpgkeys.list() def test_user_ssh_keys(gl, user, SSH_KEY): @@ -132,7 +131,6 @@ def test_user_ssh_keys(gl, user, SSH_KEY): assert get_key.key == key.key key.delete() - assert key not in user.keys.list() def test_user_email(gl, user): @@ -140,37 +138,45 @@ def test_user_email(gl, user): assert email in user.emails.list() email.delete() - assert email not in user.emails.list() def test_user_custom_attributes(gl, user): - attrs = user.customattributes.list() - assert not attrs + user.customattributes.list() attr = user.customattributes.set("key", "value1") - assert user in gl.users.list(custom_attributes={"key": "value1"}) + users_with_attribute = gl.users.list(custom_attributes={"key": "value1"}) + + assert user in users_with_attribute + assert attr.key == "key" assert attr.value == "value1" assert attr in user.customattributes.list() - attr = user.customattributes.set("key", "value2") - attr = user.customattributes.get("key") - assert attr.value == "value2" - assert attr in user.customattributes.list() + user.customattributes.set("key", "value2") + attr_2 = user.customattributes.get("key") + assert attr_2.value == "value2" + assert attr_2 in user.customattributes.list() - attr.delete() - assert attr not in user.customattributes.list() + attr_2.delete() def test_user_impersonation_tokens(gl, user): + today = datetime.date.today() + future_date = today + datetime.timedelta(days=4) + token = user.impersonationtokens.create( - {"name": "token1", "scopes": ["api", "read_user"]} + { + "name": "user_impersonation_token", + "scopes": ["api", "read_user"], + "expires_at": future_date.isoformat(), + } ) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(30) + assert token in user.impersonationtokens.list(state="active") token.delete() - assert token not in user.impersonationtokens.list(state="active") - assert token in user.impersonationtokens.list(state="inactive") def test_user_identities(gl, user): @@ -182,5 +188,3 @@ def test_user_identities(gl, user): assert provider in [item["provider"] for item in user.identities] user.identityproviders.delete(provider) - user = gl.users.get(user.id) - assert provider not in [item["provider"] for item in user.identities] diff --git a/tests/functional/api/test_variables.py b/tests/functional/api/test_variables.py index 867e563a3..eeed51da7 100644 --- a/tests/functional/api/test_variables.py +++ b/tests/functional/api/test_variables.py @@ -17,7 +17,6 @@ def test_instance_variables(gl): assert variable.value == "new_value1" variable.delete() - assert variable not in gl.variables.list() def test_group_variables(group): @@ -31,7 +30,6 @@ def test_group_variables(group): assert variable.value == "new_value1" variable.delete() - assert variable not in group.variables.list() def test_project_variables(project): @@ -45,4 +43,3 @@ def test_project_variables(project): assert variable.value == "new_value1" variable.delete() - assert variable not in project.variables.list() diff --git a/tests/functional/cli/test_cli_repository.py b/tests/functional/cli/test_cli_repository.py index c20ee9d35..0dfc5fbe8 100644 --- a/tests/functional/cli/test_cli_repository.py +++ b/tests/functional/cli/test_cli_repository.py @@ -1,5 +1,4 @@ import json -import logging import time @@ -77,25 +76,13 @@ def test_list_merge_request_commits(gitlab_cli, merge_request, project): assert ret.stdout -def test_commit_merge_requests(gitlab_cli, project, merge_request, wait_for_sidekiq): - """This tests the `project-commit merge-requests` command and also tests - that we can print the result using the `json` formatter""" - # Merge the MR first - wait_for_sidekiq(timeout=60) - - logging.info(f"MR status: {merge_request.state}") - logging.info(f"MR merge status: {merge_request.detailed_merge_status}") - - if merge_request.detailed_merge_status == "not_approved": - logging.info("Approving Merge Request") - - merge_request.approve() - - logging.info(f"MR merge status: {merge_request.detailed_merge_status}") - time.sleep(5) +def test_commit_merge_requests(gitlab_cli, project, merge_request): + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(30) merge_result = merge_request.merge(should_remove_source_branch=True) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) # Wait until it is merged mr = None @@ -108,8 +95,8 @@ def test_commit_merge_requests(gitlab_cli, project, merge_request, wait_for_side assert mr is not None assert mr.merged_at is not None - time.sleep(0.5) - wait_for_sidekiq(timeout=60) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) commit_sha = merge_result["sha"] cmd = [ diff --git a/tests/functional/cli/test_cli_resource_access_tokens.py b/tests/functional/cli/test_cli_resource_access_tokens.py index 16705764b..c080749b5 100644 --- a/tests/functional/cli/test_cli_resource_access_tokens.py +++ b/tests/functional/cli/test_cli_resource_access_tokens.py @@ -1,4 +1,4 @@ -from datetime import date +import datetime def test_list_project_access_tokens(gitlab_cli, project): @@ -19,7 +19,7 @@ def test_create_project_access_token_with_scopes(gitlab_cli, project): "--scopes", "api,read_repository", "--expires-at", - date.today().isoformat(), + datetime.date.today().isoformat(), ] ret = gitlab_cli(cmd) @@ -44,7 +44,7 @@ def test_create_group_access_token_with_scopes(gitlab_cli, group): "--scopes", "api,read_repository", "--expires-at", - date.today().isoformat(), + datetime.date.today().isoformat(), ] ret = gitlab_cli(cmd) diff --git a/tests/functional/cli/test_cli_users.py b/tests/functional/cli/test_cli_users.py index 88fe60b1b..fd1942ae1 100644 --- a/tests/functional/cli/test_cli_users.py +++ b/tests/functional/cli/test_cli_users.py @@ -1,4 +1,4 @@ -from datetime import date +import datetime def test_create_user_impersonation_token_with_scopes(gitlab_cli, user): @@ -12,7 +12,7 @@ def test_create_user_impersonation_token_with_scopes(gitlab_cli, user): "--scopes", "api,read_user", "--expires-at", - date.today().isoformat(), + datetime.date.today().isoformat(), ] ret = gitlab_cli(cmd) diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py index 7fd7ff532..1da0b1a8d 100644 --- a/tests/functional/cli/test_cli_v4.py +++ b/tests/functional/cli/test_cli_v4.py @@ -1,7 +1,6 @@ -import logging +import datetime import os import time -from datetime import date branch = "branch-cli-v4" @@ -231,7 +230,7 @@ def test_create_merge_request(gitlab_cli, project): assert ret.success -def test_accept_request_merge(gitlab_cli, project, wait_for_sidekiq): +def test_accept_request_merge(gitlab_cli, project): # MR needs at least 1 commit before we can merge mr = project.mergerequests.list()[0] file_data = { @@ -241,15 +240,10 @@ def test_accept_request_merge(gitlab_cli, project, wait_for_sidekiq): "commit_message": "chore: test-cli-v4 change", } project.files.create(file_data) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) time.sleep(30) - wait_for_sidekiq(timeout=60) - - logging.info(f"MR status: {mr.state}") - logging.info(f"MR merge status: {mr.detailed_merge_status}") if mr.detailed_merge_status == "not_approved": - logging.info("Approving Merge Request") - approve_cmd = [ "project-merge-request", "approve", @@ -260,10 +254,9 @@ def test_accept_request_merge(gitlab_cli, project, wait_for_sidekiq): ] gitlab_cli(approve_cmd) + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) time.sleep(5) - logging.info(f"MR merge status: {mr.detailed_merge_status}") - time.sleep(0.5) approve_cmd = [ "project-merge-request", "merge", @@ -586,7 +579,7 @@ def test_create_project_with_values_at_prefixed(gitlab_cli, tmpdir): def test_create_project_deploy_token(gitlab_cli, project): name = "project-token" username = "root" - expires_at = date.today().isoformat() + expires_at = datetime.date.today().isoformat() scopes = "read_registry" cmd = [ @@ -662,7 +655,7 @@ def test_delete_project_deploy_token(gitlab_cli, deploy_token): def test_create_group_deploy_token(gitlab_cli, group): name = "group-token" username = "root" - expires_at = date.today().isoformat() + expires_at = datetime.date.today().isoformat() scopes = "read_registry" cmd = [ diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index 62304a411..f21f20b53 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -1,10 +1,10 @@ import dataclasses +import datetime import logging import pathlib import tempfile import time import uuid -from datetime import date from subprocess import check_output from typing import Optional @@ -208,42 +208,6 @@ def _check( return _check -@pytest.fixture -def wait_for_sidekiq(gl): - """ - Return a helper function to wait until there are spare sidekiq processes. - - Because not all Groups can be deleted immediately in GL 16, we will likely have busy Sidekiq jobs - set aside for their deletion in 1 days time. - - Use this with asserts for slow tasks (group/project/user creation/deletion). - - { ..., 'labels': [], 'concurrency': 20, 'busy': 3} - """ - - def _wait(timeout: int = 30, step: float = 0.5, allow_fail: bool = False) -> bool: - for count in range(timeout): - time.sleep(step) - processes = gl.sidekiq.process_metrics()["processes"] - busy_processes = 0 - total_concurrency = 0 - for process in processes: - busy_processes += process["busy"] - total_concurrency += process["concurrency"] - - logging.info(f"Busy processes {busy_processes}") - # If we have space concurrency in the process, continue - if busy_processes < total_concurrency: - logging.info("Spare sidekiq process found") - return True - - logging.info(f"sidekiq busy {count} of {timeout}") - assert allow_fail, "sidekiq process should have terminated but did not." - return False - - return _wait - - @pytest.fixture(scope="session") def gitlab_token( check_is_alive, @@ -386,7 +350,7 @@ def project(gl): @pytest.fixture(scope="function") -def make_merge_request(project, wait_for_sidekiq): +def make_merge_request(project): """Fixture factory used to create a merge_request. It will create a branch, add a commit to the branch, and then create a @@ -406,8 +370,8 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): # NOTE(jlvillal): Sometimes the CI would give a "500 Internal Server # Error". Hoping that waiting until all other processes are done will # help with that. - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(30) project.refresh() # Gets us the current default branch logging.info(f"Creating branch {source_branch}") @@ -426,8 +390,7 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): ) # Helps with Debugging why MRs fail to merge resulting in 405 from downstream tests - approval_rules = project.approvalrules.list() - logging.info(f"Project MR Approval Rules {approval_rules}") + project.approvalrules.list() if create_pipeline: project.files.create( @@ -444,7 +407,6 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): "commit_message": "Add a simple pipeline", } ) - logging.info(f"Creating merge request for {source_branch}") mr = project.mergerequests.create( { "source_branch": source_branch, @@ -453,15 +415,13 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): "remove_source_branch": True, } ) - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" + + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) + time.sleep(5) mr_iid = mr.iid for _ in range(60): mr = project.mergerequests.get(mr_iid) - logging.info( - f"Waiting for Gitlab to update MR status: {mr.detailed_merge_status}" - ) if ( mr.detailed_merge_status == "checking" or mr.detailed_merge_status == "unchecked" @@ -624,7 +584,7 @@ def deploy_token(project): data = { "name": f"token-{_id}", "username": "root", - "expires_at": date.today().isoformat(), + "expires_at": datetime.date.today().isoformat(), "scopes": "read_registry", } @@ -638,7 +598,7 @@ def group_deploy_token(group): data = { "name": f"group-token-{_id}", "username": "root", - "expires_at": date.today().isoformat(), + "expires_at": datetime.date.today().isoformat(), "scopes": "read_registry", } diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env index ce8680499..05d90fa61 100644 --- a/tests/functional/fixtures/.env +++ b/tests/functional/fixtures/.env @@ -1,2 +1,2 @@ GITLAB_IMAGE=gitlab/gitlab-ee -GITLAB_TAG=16.8.1-ee.0 +GITLAB_TAG=16.10.1-ee.0 diff --git a/tests/functional/helpers.py b/tests/functional/helpers.py index c9dae1619..a898aa947 100644 --- a/tests/functional/helpers.py +++ b/tests/functional/helpers.py @@ -36,12 +36,10 @@ def safe_delete(object: gitlab.base.RESTObject) -> None: except gitlab.exceptions.GitlabGetError: return - logging.info(f"Deleting object type {type(object)}") - if index: logging.info(f"Attempt {index + 1} to delete {object!r}.") try: - if type(object) is gitlab.v4.objects.User: + if isinstance(object, gitlab.v4.objects.User): # You can't use this option if the selected user is the sole owner of any groups # Use `hard_delete=True` or a 'Ghost User' may be created. # https://docs.gitlab.com/ee/api/users.html#user-deletion @@ -51,7 +49,7 @@ def safe_delete(object: gitlab.base.RESTObject) -> None: # which combined with parents group never immediately deleting in GL 16 # we shouldn't cause test to fail if it still exists return - elif type(object) is gitlab.v4.objects.Project: + elif isinstance(object, gitlab.v4.objects.Project): # Immediately delete rather than waiting for at least 1day # https://docs.gitlab.com/ee/api/projects.html#delete-project object.delete(permanently_remove=True) @@ -59,11 +57,11 @@ def safe_delete(object: gitlab.base.RESTObject) -> None: else: # We only attempt to delete parent groups to prevent dangling sub-groups # However parent groups can only be deleted on a delay in Gl 16 - # https://docs.gitlab.com/ee/api/projects.html#delete-group + # https://docs.gitlab.com/ee/api/groups.html#remove-group object.delete() except gitlab.exceptions.GitlabDeleteError: logging.info(f"{object!r} already deleted or scheduled for deletion.") - if type(object) is gitlab.v4.objects.Group: + if isinstance(object, gitlab.v4.objects.Group): # Parent groups can never be immediately deleted in GL 16, # so don't cause test to fail if it still exists return From 7599d83bd9149348bb84fba87dab0b358d7a710b Mon Sep 17 00:00:00 2001 From: Tim Knight Date: Wed, 24 Apr 2024 10:26:12 +0100 Subject: [PATCH 6/8] test: tidy up functional tests Signed-off-by: Tim Knight --- tests/functional/api/test_deploy_tokens.py | 2 +- tests/functional/cli/test_cli_repository.py | 3 +++ tests/functional/cli/test_cli_v4.py | 4 +++- tests/functional/conftest.py | 3 --- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/functional/api/test_deploy_tokens.py b/tests/functional/api/test_deploy_tokens.py index 3dcaf127e..0b506e078 100644 --- a/tests/functional/api/test_deploy_tokens.py +++ b/tests/functional/api/test_deploy_tokens.py @@ -16,7 +16,7 @@ def test_project_deploy_tokens(gl, project): deploy_token = project.deploytokens.get(deploy_token.id) assert deploy_token.name == "foo" - assert deploy_token.expires_at == today + "T00:00:00.000Z" + assert deploy_token.expires_at == f"{today}T00:00:00.000Z" assert deploy_token.scopes == ["read_registry"] assert deploy_token.username == "bar" diff --git a/tests/functional/cli/test_cli_repository.py b/tests/functional/cli/test_cli_repository.py index 0dfc5fbe8..d6bd1d2e4 100644 --- a/tests/functional/cli/test_cli_repository.py +++ b/tests/functional/cli/test_cli_repository.py @@ -77,6 +77,9 @@ def test_list_merge_request_commits(gitlab_cli, merge_request, project): def test_commit_merge_requests(gitlab_cli, project, merge_request): + """This tests the `project-commit merge-requests` command and also tests + that we can print the result using the `json` formatter""" + # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) time.sleep(30) diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py index 1da0b1a8d..14214ffa6 100644 --- a/tests/functional/cli/test_cli_v4.py +++ b/tests/functional/cli/test_cli_v4.py @@ -2,7 +2,7 @@ import os import time -branch = "branch-cli-v4" +branch = "BRANCH-cli-v4" def test_create_project(gitlab_cli): @@ -243,6 +243,8 @@ def test_accept_request_merge(gitlab_cli, project): # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) time.sleep(30) + mr = project.mergerequests.get(mr.iid) + if mr.detailed_merge_status == "not_approved": approve_cmd = [ "project-merge-request", diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py index f21f20b53..f2f31e52f 100644 --- a/tests/functional/conftest.py +++ b/tests/functional/conftest.py @@ -389,9 +389,6 @@ def _make_merge_request(*, source_branch: str, create_pipeline: bool = False): } ) - # Helps with Debugging why MRs fail to merge resulting in 405 from downstream tests - project.approvalrules.list() - if create_pipeline: project.files.create( { From f7c085eee1b5e87f04f4bf520692b6002d8d140b Mon Sep 17 00:00:00 2001 From: Tim Knight Date: Wed, 24 Apr 2024 11:28:06 +0100 Subject: [PATCH 7/8] docs: how to run smoke tests Signed-off-by: Tim Knight --- CONTRIBUTING.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ed1ed3849..8433be243 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -75,6 +75,9 @@ You need to install ``tox`` (``pip3 install tox``) to run tests and lint checks # run unit tests in one python environment only (useful for quick testing during development): tox -e py311 + # run unit and smoke tests in one python environment only + tox -e py312,smoke + # build the documentation - the result will be generated in build/sphinx/html/: tox -e docs From f9d915941e26cc13738447036951ec05527aaaf7 Mon Sep 17 00:00:00 2001 From: Tim Knight Date: Wed, 24 Apr 2024 12:09:29 +0100 Subject: [PATCH 8/8] test: remove approve step Signed-off-by: Tim Knight --- tests/functional/cli/test_cli_v4.py | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py index 14214ffa6..4a0d07a08 100644 --- a/tests/functional/cli/test_cli_v4.py +++ b/tests/functional/cli/test_cli_v4.py @@ -243,22 +243,6 @@ def test_accept_request_merge(gitlab_cli, project): # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) time.sleep(30) - mr = project.mergerequests.get(mr.iid) - - if mr.detailed_merge_status == "not_approved": - approve_cmd = [ - "project-merge-request", - "approve", - "--project-id", - project.id, - "--iid", - mr.iid, - ] - gitlab_cli(approve_cmd) - - # Pause to let GL catch up (happens on hosted too, sometimes takes a while for server to be ready to merge) - time.sleep(5) - approve_cmd = [ "project-merge-request", "merge",