From 502715d99e02105c39b2c5cf0e7457b3256eba0d Mon Sep 17 00:00:00 2001 From: "John L. Villalovos" Date: Tue, 25 May 2021 17:35:17 -0700 Subject: chore: rename 'tools/functional/' to 'tests/functional/' Rename the 'tools/functional/' directory to 'tests/functional/' This makes more sense as these are functional tests and not tools. This was dicussed in: https://github.com/python-gitlab/python-gitlab/discussions/1468 --- .renovaterc.json | 2 +- MANIFEST.in | 2 +- tests/functional/api/test_clusters.py | 46 ++ tests/functional/api/test_current_user.py | 42 ++ tests/functional/api/test_deploy_keys.py | 12 + tests/functional/api/test_deploy_tokens.py | 36 ++ tests/functional/api/test_gitlab.py | 183 +++++++ tests/functional/api/test_groups.py | 195 ++++++++ tests/functional/api/test_import_export.py | 66 +++ tests/functional/api/test_issues.py | 93 ++++ tests/functional/api/test_merge_requests.py | 165 +++++++ tests/functional/api/test_packages.py | 13 + tests/functional/api/test_projects.py | 267 ++++++++++ tests/functional/api/test_releases.py | 36 ++ tests/functional/api/test_repository.py | 126 +++++ tests/functional/api/test_snippets.py | 74 +++ tests/functional/api/test_users.py | 170 +++++++ tests/functional/api/test_variables.py | 48 ++ tests/functional/cli/conftest.py | 21 + tests/functional/cli/test_cli_artifacts.py | 51 ++ tests/functional/cli/test_cli_packages.py | 12 + tests/functional/cli/test_cli_v4.py | 715 +++++++++++++++++++++++++++ tests/functional/cli/test_cli_variables.py | 19 + tests/functional/conftest.py | 462 +++++++++++++++++ tests/functional/ee-test.py | 158 ++++++ tests/functional/fixtures/.env | 2 + tests/functional/fixtures/avatar.png | Bin 0 -> 592 bytes tests/functional/fixtures/docker-compose.yml | 46 ++ tests/functional/fixtures/set_token.rb | 9 + tools/functional/api/test_clusters.py | 46 -- tools/functional/api/test_current_user.py | 42 -- tools/functional/api/test_deploy_keys.py | 12 - tools/functional/api/test_deploy_tokens.py | 36 -- tools/functional/api/test_gitlab.py | 183 ------- tools/functional/api/test_groups.py | 195 -------- tools/functional/api/test_import_export.py | 66 --- tools/functional/api/test_issues.py | 93 ---- tools/functional/api/test_merge_requests.py | 165 ------- tools/functional/api/test_packages.py | 13 - tools/functional/api/test_projects.py | 267 ---------- tools/functional/api/test_releases.py | 36 -- tools/functional/api/test_repository.py | 126 ----- tools/functional/api/test_snippets.py | 74 --- tools/functional/api/test_users.py | 170 ------- tools/functional/api/test_variables.py | 48 -- tools/functional/cli/conftest.py | 21 - tools/functional/cli/test_cli_artifacts.py | 51 -- tools/functional/cli/test_cli_packages.py | 12 - tools/functional/cli/test_cli_v4.py | 715 --------------------------- tools/functional/cli/test_cli_variables.py | 19 - tools/functional/conftest.py | 462 ----------------- tools/functional/ee-test.py | 158 ------ tools/functional/fixtures/.env | 2 - tools/functional/fixtures/avatar.png | Bin 592 -> 0 bytes tools/functional/fixtures/docker-compose.yml | 46 -- tools/functional/fixtures/set_token.rb | 9 - tox.ini | 4 +- 57 files changed, 3071 insertions(+), 3071 deletions(-) create mode 100644 tests/functional/api/test_clusters.py create mode 100644 tests/functional/api/test_current_user.py create mode 100644 tests/functional/api/test_deploy_keys.py create mode 100644 tests/functional/api/test_deploy_tokens.py create mode 100644 tests/functional/api/test_gitlab.py create mode 100644 tests/functional/api/test_groups.py create mode 100644 tests/functional/api/test_import_export.py create mode 100644 tests/functional/api/test_issues.py create mode 100644 tests/functional/api/test_merge_requests.py create mode 100644 tests/functional/api/test_packages.py create mode 100644 tests/functional/api/test_projects.py create mode 100644 tests/functional/api/test_releases.py create mode 100644 tests/functional/api/test_repository.py create mode 100644 tests/functional/api/test_snippets.py create mode 100644 tests/functional/api/test_users.py create mode 100644 tests/functional/api/test_variables.py create mode 100644 tests/functional/cli/conftest.py create mode 100644 tests/functional/cli/test_cli_artifacts.py create mode 100644 tests/functional/cli/test_cli_packages.py create mode 100644 tests/functional/cli/test_cli_v4.py create mode 100644 tests/functional/cli/test_cli_variables.py create mode 100644 tests/functional/conftest.py create mode 100755 tests/functional/ee-test.py create mode 100644 tests/functional/fixtures/.env create mode 100644 tests/functional/fixtures/avatar.png create mode 100644 tests/functional/fixtures/docker-compose.yml create mode 100644 tests/functional/fixtures/set_token.rb delete mode 100644 tools/functional/api/test_clusters.py delete mode 100644 tools/functional/api/test_current_user.py delete mode 100644 tools/functional/api/test_deploy_keys.py delete mode 100644 tools/functional/api/test_deploy_tokens.py delete mode 100644 tools/functional/api/test_gitlab.py delete mode 100644 tools/functional/api/test_groups.py delete mode 100644 tools/functional/api/test_import_export.py delete mode 100644 tools/functional/api/test_issues.py delete mode 100644 tools/functional/api/test_merge_requests.py delete mode 100644 tools/functional/api/test_packages.py delete mode 100644 tools/functional/api/test_projects.py delete mode 100644 tools/functional/api/test_releases.py delete mode 100644 tools/functional/api/test_repository.py delete mode 100644 tools/functional/api/test_snippets.py delete mode 100644 tools/functional/api/test_users.py delete mode 100644 tools/functional/api/test_variables.py delete mode 100644 tools/functional/cli/conftest.py delete mode 100644 tools/functional/cli/test_cli_artifacts.py delete mode 100644 tools/functional/cli/test_cli_packages.py delete mode 100644 tools/functional/cli/test_cli_v4.py delete mode 100644 tools/functional/cli/test_cli_variables.py delete mode 100644 tools/functional/conftest.py delete mode 100755 tools/functional/ee-test.py delete mode 100644 tools/functional/fixtures/.env delete mode 100644 tools/functional/fixtures/avatar.png delete mode 100644 tools/functional/fixtures/docker-compose.yml delete mode 100644 tools/functional/fixtures/set_token.rb diff --git a/.renovaterc.json b/.renovaterc.json index b46c8f4..1ddc2e8 100644 --- a/.renovaterc.json +++ b/.renovaterc.json @@ -4,7 +4,7 @@ ], "regexManagers": [ { - "fileMatch": ["^tools/functional/fixtures/.env$"], + "fileMatch": ["^tests/functional/fixtures/.env$"], "matchStrings": ["GITLAB_TAG=(?.*?)\n"], "depNameTemplate": "gitlab/gitlab-ce", "datasourceTemplate": "docker", diff --git a/MANIFEST.in b/MANIFEST.in index df53d66..849cc7d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ include COPYING AUTHORS ChangeLog.rst RELEASE_NOTES.rst requirements.txt test-requirements.txt rtd-requirements.txt include tox.ini .travis.yml -recursive-include tools * +recursive-include tests * recursive-include docs *j2 *.py *.rst api/*.rst Makefile make.bat recursive-include gitlab/tests/data * diff --git a/tests/functional/api/test_clusters.py b/tests/functional/api/test_clusters.py new file mode 100644 index 0000000..8930aad --- /dev/null +++ b/tests/functional/api/test_clusters.py @@ -0,0 +1,46 @@ +def test_project_clusters(project): + project.clusters.create( + { + "name": "cluster1", + "platform_kubernetes_attributes": { + "api_url": "http://url", + "token": "tokenval", + }, + } + ) + clusters = project.clusters.list() + assert len(clusters) == 1 + + cluster = clusters[0] + cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} + cluster.save() + + cluster = project.clusters.list()[0] + assert cluster.platform_kubernetes["api_url"] == "http://newurl" + + cluster.delete() + assert len(project.clusters.list()) == 0 + + +def test_group_clusters(group): + group.clusters.create( + { + "name": "cluster1", + "platform_kubernetes_attributes": { + "api_url": "http://url", + "token": "tokenval", + }, + } + ) + clusters = group.clusters.list() + assert len(clusters) == 1 + + cluster = clusters[0] + cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} + cluster.save() + + cluster = group.clusters.list()[0] + assert cluster.platform_kubernetes["api_url"] == "http://newurl" + + cluster.delete() + assert len(group.clusters.list()) == 0 diff --git a/tests/functional/api/test_current_user.py b/tests/functional/api/test_current_user.py new file mode 100644 index 0000000..5802457 --- /dev/null +++ b/tests/functional/api/test_current_user.py @@ -0,0 +1,42 @@ +def test_current_user_email(gl): + gl.auth() + mail = gl.user.emails.create({"email": "current@user.com"}) + assert len(gl.user.emails.list()) == 1 + + mail.delete() + assert len(gl.user.emails.list()) == 0 + + +def test_current_user_gpg_keys(gl, GPG_KEY): + gl.auth() + gkey = gl.user.gpgkeys.create({"key": GPG_KEY}) + assert len(gl.user.gpgkeys.list()) == 1 + + # Seems broken on the gitlab side + gkey = gl.user.gpgkeys.get(gkey.id) + gkey.delete() + assert len(gl.user.gpgkeys.list()) == 0 + + +def test_current_user_ssh_keys(gl, SSH_KEY): + gl.auth() + key = gl.user.keys.create({"title": "testkey", "key": SSH_KEY}) + assert len(gl.user.keys.list()) == 1 + + key.delete() + assert len(gl.user.keys.list()) == 0 + + +def test_current_user_status(gl): + gl.auth() + message = "Test" + emoji = "thumbsup" + status = gl.user.status.get() + + status.message = message + status.emoji = emoji + status.save() + + new_status = gl.user.status.get() + assert new_status.message == message + assert new_status.emoji == emoji diff --git a/tests/functional/api/test_deploy_keys.py b/tests/functional/api/test_deploy_keys.py new file mode 100644 index 0000000..18828a2 --- /dev/null +++ b/tests/functional/api/test_deploy_keys.py @@ -0,0 +1,12 @@ +def test_project_deploy_keys(gl, project, DEPLOY_KEY): + deploy_key = project.keys.create({"title": "foo@bar", "key": DEPLOY_KEY}) + project_keys = list(project.keys.list()) + assert len(project_keys) == 1 + + project2 = gl.projects.create({"name": "deploy-key-project"}) + project2.keys.enable(deploy_key.id) + assert len(project2.keys.list()) == 1 + + project2.keys.delete(deploy_key.id) + assert len(project2.keys.list()) == 0 + project2.delete() diff --git a/tests/functional/api/test_deploy_tokens.py b/tests/functional/api/test_deploy_tokens.py new file mode 100644 index 0000000..efcf8b1 --- /dev/null +++ b/tests/functional/api/test_deploy_tokens.py @@ -0,0 +1,36 @@ +def test_project_deploy_tokens(gl, project): + deploy_token = project.deploytokens.create( + { + "name": "foo", + "username": "bar", + "expires_at": "2022-01-01", + "scopes": ["read_registry"], + } + ) + assert len(project.deploytokens.list()) == 1 + assert gl.deploytokens.list() == project.deploytokens.list() + + assert project.deploytokens.list()[0].name == "foo" + assert project.deploytokens.list()[0].expires_at == "2022-01-01T00:00:00.000Z" + assert project.deploytokens.list()[0].scopes == ["read_registry"] + assert project.deploytokens.list()[0].username == "bar" + + deploy_token.delete() + assert len(project.deploytokens.list()) == 0 + assert len(gl.deploytokens.list()) == 0 + + +def test_group_deploy_tokens(gl, group): + deploy_token = group.deploytokens.create( + { + "name": "foo", + "scopes": ["read_registry"], + } + ) + + assert len(group.deploytokens.list()) == 1 + assert gl.deploytokens.list() == group.deploytokens.list() + + deploy_token.delete() + assert len(group.deploytokens.list()) == 0 + assert len(gl.deploytokens.list()) == 0 diff --git a/tests/functional/api/test_gitlab.py b/tests/functional/api/test_gitlab.py new file mode 100644 index 0000000..7a70a56 --- /dev/null +++ b/tests/functional/api/test_gitlab.py @@ -0,0 +1,183 @@ +import pytest + +import gitlab + + +def test_auth_from_config(gl, temp_dir): + """Test token authentication from config file""" + test_gitlab = gitlab.Gitlab.from_config( + config_files=[temp_dir / "python-gitlab.cfg"] + ) + test_gitlab.auth() + assert isinstance(test_gitlab.user, gitlab.v4.objects.CurrentUser) + + +def test_broadcast_messages(gl): + msg = gl.broadcastmessages.create({"message": "this is the message"}) + msg.color = "#444444" + msg.save() + msg_id = msg.id + + msg = gl.broadcastmessages.list(all=True)[0] + assert msg.color == "#444444" + + msg = gl.broadcastmessages.get(msg_id) + assert msg.color == "#444444" + + msg.delete() + assert len(gl.broadcastmessages.list()) == 0 + + +def test_markdown(gl): + html = gl.markdown("foo") + assert "foo" in html + + +def test_lint(gl): + success, errors = gl.lint("Invalid") + assert success is False + assert errors + + +def test_sidekiq_queue_metrics(gl): + out = gl.sidekiq.queue_metrics() + assert isinstance(out, dict) + assert "pages" in out["queues"] + + +def test_sidekiq_process_metrics(gl): + out = gl.sidekiq.process_metrics() + assert isinstance(out, dict) + assert "hostname" in out["processes"][0] + + +def test_sidekiq_job_stats(gl): + out = gl.sidekiq.job_stats() + assert isinstance(out, dict) + assert "processed" in out["jobs"] + + +def test_sidekiq_compound_metrics(gl): + out = gl.sidekiq.compound_metrics() + assert isinstance(out, dict) + assert "jobs" in out + assert "processes" in out + assert "queues" in out + + +def test_gitlab_settings(gl): + settings = gl.settings.get() + settings.default_projects_limit = 42 + settings.save() + settings = gl.settings.get() + assert settings.default_projects_limit == 42 + + +def test_template_dockerfile(gl): + assert gl.dockerfiles.list() + + dockerfile = gl.dockerfiles.get("Node") + assert dockerfile.content is not None + + +def test_template_gitignore(gl): + assert gl.gitignores.list() + gitignore = gl.gitignores.get("Node") + assert gitignore.content is not None + + +def test_template_gitlabciyml(gl): + assert gl.gitlabciymls.list() + gitlabciyml = gl.gitlabciymls.get("Nodejs") + assert gitlabciyml.content is not None + + +def test_template_license(gl): + assert gl.licenses.list() + license = gl.licenses.get( + "bsd-2-clause", project="mytestproject", fullname="mytestfullname" + ) + assert "mytestfullname" in license.content + + +def test_hooks(gl): + hook = gl.hooks.create({"url": "http://whatever.com"}) + assert len(gl.hooks.list()) == 1 + + hook.delete() + assert len(gl.hooks.list()) == 0 + + +def test_namespaces(gl): + namespace = gl.namespaces.list(all=True) + assert namespace + + namespace = gl.namespaces.list(search="root", all=True)[0] + assert namespace.kind == "user" + + +def test_notification_settings(gl): + settings = gl.notificationsettings.get() + settings.level = gitlab.NOTIFICATION_LEVEL_WATCH + settings.save() + + settings = gl.notificationsettings.get() + assert settings.level == gitlab.NOTIFICATION_LEVEL_WATCH + + +def test_user_activities(gl): + activities = gl.user_activities.list(query_parameters={"from": "2019-01-01"}) + assert isinstance(activities, list) + + +def test_events(gl): + events = gl.events.list() + assert isinstance(events, list) + + +@pytest.mark.skip +def test_features(gl): + feat = gl.features.set("foo", 30) + assert feat.name == "foo" + assert len(gl.features.list()) == 1 + + feat.delete() + assert len(gl.features.list()) == 0 + + +def test_pagination(gl, project): + project2 = gl.projects.create({"name": "project-page-2"}) + + list1 = gl.projects.list(per_page=1, page=1) + list2 = gl.projects.list(per_page=1, page=2) + assert len(list1) == 1 + assert len(list2) == 1 + assert list1[0].id != list2[0].id + + project2.delete() + + +def test_rate_limits(gl): + settings = gl.settings.get() + settings.throttle_authenticated_api_enabled = True + settings.throttle_authenticated_api_requests_per_period = 1 + settings.throttle_authenticated_api_period_in_seconds = 3 + settings.save() + + projects = list() + for i in range(0, 20): + projects.append(gl.projects.create({"name": str(i) + "ok"})) + + with pytest.raises(gitlab.GitlabCreateError) as e: + for i in range(20, 40): + projects.append( + gl.projects.create( + {"name": str(i) + "shouldfail"}, obey_rate_limit=False + ) + ) + + assert "Retry later" in str(e.value) + + settings.throttle_authenticated_api_enabled = False + settings.save() + [project.delete() for project in projects] diff --git a/tests/functional/api/test_groups.py b/tests/functional/api/test_groups.py new file mode 100644 index 0000000..eae2d9b --- /dev/null +++ b/tests/functional/api/test_groups.py @@ -0,0 +1,195 @@ +import pytest + +import gitlab + + +def test_groups(gl): + # TODO: This one still needs lots of work + user = gl.users.create( + { + "email": "user@test.com", + "username": "user", + "name": "user", + "password": "user_pass", + } + ) + user2 = gl.users.create( + { + "email": "user2@test.com", + "username": "user2", + "name": "user2", + "password": "user2_pass", + } + ) + group1 = gl.groups.create({"name": "group1", "path": "group1"}) + group2 = gl.groups.create({"name": "group2", "path": "group2"}) + + p_id = gl.groups.list(search="group2")[0].id + group3 = gl.groups.create({"name": "group3", "path": "group3", "parent_id": p_id}) + group4 = gl.groups.create({"name": "group4", "path": "group4"}) + + assert len(gl.groups.list()) == 4 + assert len(gl.groups.list(search="oup1")) == 1 + assert group3.parent_id == p_id + assert group2.subgroups.list()[0].id == group3.id + + filtered_groups = gl.groups.list(skip_groups=[group3.id, group4.id]) + assert group3 not in filtered_groups + assert group3 not in filtered_groups + + group1.members.create( + {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user.id} + ) + group1.members.create( + {"access_level": gitlab.const.GUEST_ACCESS, "user_id": user2.id} + ) + group2.members.create( + {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user2.id} + ) + + group4.share(group1.id, gitlab.const.DEVELOPER_ACCESS) + group4.share(group2.id, gitlab.const.MAINTAINER_ACCESS) + # Reload group4 to have updated shared_with_groups + group4 = gl.groups.get(group4.id) + assert len(group4.shared_with_groups) == 2 + group4.unshare(group1.id) + # Reload group4 to have updated shared_with_groups + group4 = gl.groups.get(group4.id) + assert len(group4.shared_with_groups) == 1 + + # User memberships (admin only) + memberships1 = user.memberships.list() + assert len(memberships1) == 1 + + memberships2 = user2.memberships.list() + assert len(memberships2) == 2 + + membership = memberships1[0] + assert membership.source_type == "Namespace" + assert membership.access_level == gitlab.const.OWNER_ACCESS + + project_memberships = user.memberships.list(type="Project") + assert len(project_memberships) == 0 + + group_memberships = user.memberships.list(type="Namespace") + assert len(group_memberships) == 1 + + with pytest.raises(gitlab.GitlabListError) as e: + membership = user.memberships.list(type="Invalid") + assert "type does not have a valid value" in str(e.value) + + with pytest.raises(gitlab.GitlabListError) as e: + user.memberships.list(sudo=user.name) + assert "403 Forbidden" in str(e.value) + + # Administrator belongs to the groups + assert len(group1.members.list()) == 3 + assert len(group2.members.list()) == 2 + + group1.members.delete(user.id) + assert len(group1.members.list()) == 2 + assert len(group1.members.all()) # Deprecated + assert len(group1.members_all.list()) + member = group1.members.get(user2.id) + member.access_level = gitlab.const.OWNER_ACCESS + member.save() + member = group1.members.get(user2.id) + assert member.access_level == gitlab.const.OWNER_ACCESS + + group2.members.delete(gl.user.id) + + +@pytest.mark.skip(reason="Commented out in legacy test") +def test_group_labels(group): + group.labels.create({"name": "foo", "description": "bar", "color": "#112233"}) + label = group.labels.get("foo") + assert label.description == "bar" + + label.description = "baz" + label.save() + label = group.labels.get("foo") + assert label.description == "baz" + assert len(group.labels.list()) == 1 + + label.delete() + assert len(group.labels.list()) == 0 + + +def test_group_notification_settings(group): + settings = group.notificationsettings.get() + settings.level = "disabled" + settings.save() + + settings = group.notificationsettings.get() + assert settings.level == "disabled" + + +def test_group_badges(group): + badge_image = "http://example.com" + badge_link = "http://example/img.svg" + badge = group.badges.create({"link_url": badge_link, "image_url": badge_image}) + assert len(group.badges.list()) == 1 + + badge.image_url = "http://another.example.com" + badge.save() + + badge = group.badges.get(badge.id) + assert badge.image_url == "http://another.example.com" + + badge.delete() + assert len(group.badges.list()) == 0 + + +def test_group_milestones(group): + milestone = group.milestones.create({"title": "groupmilestone1"}) + assert len(group.milestones.list()) == 1 + + milestone.due_date = "2020-01-01T00:00:00Z" + milestone.save() + milestone.state_event = "close" + milestone.save() + + milestone = group.milestones.get(milestone.id) + assert milestone.state == "closed" + assert len(milestone.issues()) == 0 + assert len(milestone.merge_requests()) == 0 + + +def test_group_custom_attributes(gl, group): + attrs = group.customattributes.list() + assert len(attrs) == 0 + + attr = group.customattributes.set("key", "value1") + assert len(gl.groups.list(custom_attributes={"key": "value1"})) == 1 + assert attr.key == "key" + assert attr.value == "value1" + assert len(group.customattributes.list()) == 1 + + attr = group.customattributes.set("key", "value2") + attr = group.customattributes.get("key") + assert attr.value == "value2" + assert len(group.customattributes.list()) == 1 + + attr.delete() + assert len(group.customattributes.list()) == 0 + + +def test_group_subgroups_projects(gl, user): + # TODO: fixture factories + group1 = gl.groups.list(search="group1")[0] + group2 = gl.groups.list(search="group2")[0] + + group3 = gl.groups.create( + {"name": "subgroup1", "path": "subgroup1", "parent_id": group1.id} + ) + group4 = gl.groups.create( + {"name": "subgroup2", "path": "subgroup2", "parent_id": group2.id} + ) + + gr1_project = gl.projects.create({"name": "gr1_project", "namespace_id": group1.id}) + gr2_project = gl.projects.create({"name": "gr2_project", "namespace_id": group3.id}) + + assert group3.parent_id == group1.id + assert group4.parent_id == group2.id + assert gr1_project.namespace["id"] == group1.id + assert gr2_project.namespace["parent_id"] == group1.id diff --git a/tests/functional/api/test_import_export.py b/tests/functional/api/test_import_export.py new file mode 100644 index 0000000..d4bdd19 --- /dev/null +++ b/tests/functional/api/test_import_export.py @@ -0,0 +1,66 @@ +import time + +import gitlab + + +def test_group_import_export(gl, group, temp_dir): + export = group.exports.create() + assert export.message == "202 Accepted" + + # We cannot check for export_status with group export API + time.sleep(10) + + import_archive = temp_dir / "gitlab-group-export.tgz" + import_path = "imported_group" + import_name = "Imported Group" + + with open(import_archive, "wb") as f: + export.download(streamed=True, action=f.write) + + with open(import_archive, "rb") as f: + output = gl.groups.import_group(f, import_path, import_name) + assert output["message"] == "202 Accepted" + + # We cannot check for returned ID with group import API + time.sleep(10) + group_import = gl.groups.get(import_path) + + assert group_import.path == import_path + assert group_import.name == import_name + + +def test_project_import_export(gl, project, temp_dir): + export = project.exports.create() + assert export.message == "202 Accepted" + + export = project.exports.get() + assert isinstance(export, gitlab.v4.objects.ProjectExport) + + count = 0 + while export.export_status != "finished": + time.sleep(1) + export.refresh() + count += 1 + if count == 15: + raise Exception("Project export taking too much time") + + with open(temp_dir / "gitlab-export.tgz", "wb") as f: + export.download(streamed=True, action=f.write) + + output = gl.projects.import_project( + open(temp_dir / "gitlab-export.tgz", "rb"), + "imported_project", + name="Imported Project", + ) + project_import = gl.projects.get(output["id"], lazy=True).imports.get() + + assert project_import.path == "imported_project" + assert project_import.name == "Imported Project" + + count = 0 + while project_import.import_status != "finished": + time.sleep(1) + project_import.refresh() + count += 1 + if count == 15: + raise Exception("Project import taking too much time") diff --git a/tests/functional/api/test_issues.py b/tests/functional/api/test_issues.py new file mode 100644 index 0000000..f3a606b --- /dev/null +++ b/tests/functional/api/test_issues.py @@ -0,0 +1,93 @@ +import gitlab + + +def test_create_issue(project): + issue = project.issues.create({"title": "my issue 1"}) + issue2 = project.issues.create({"title": "my issue 2"}) + issue_ids = [issue.id for issue in project.issues.list()] + assert len(issue_ids) == 2 + + # Test 'iids' as a list + assert len(project.issues.list(iids=issue_ids)) == 2 + + issue2.state_event = "close" + issue2.save() + assert len(project.issues.list(state="closed")) == 1 + assert len(project.issues.list(state="opened")) == 1 + + 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 + + +def test_issue_notes(issue): + size = len(issue.notes.list()) + + note = issue.notes.create({"body": "This is an issue note"}) + assert len(issue.notes.list()) == size + 1 + + emoji = note.awardemojis.create({"name": "tractor"}) + assert len(note.awardemojis.list()) == 1 + + emoji.delete() + assert len(note.awardemojis.list()) == 0 + + note.delete() + assert len(issue.notes.list()) == size + + +def test_issue_labels(project, issue): + project.labels.create({"name": "label2", "color": "#aabbcc"}) + issue.labels = ["label2"] + issue.save() + + assert issue in project.issues.list(labels=["label2"]) + assert issue in project.issues.list(labels="label2") + assert issue in project.issues.list(labels="Any") + assert issue not in project.issues.list(labels="None") + + +def test_issue_events(issue): + events = issue.resourcelabelevents.list() + assert isinstance(events, list) + + event = issue.resourcelabelevents.get(events[0].id) + assert isinstance(event, gitlab.v4.objects.ProjectIssueResourceLabelEvent) + + +def test_issue_milestones(project, milestone): + data = {"title": "my issue 1", "milestone_id": milestone.id} + issue = project.issues.create(data) + assert milestone.issues().next().title == "my issue 1" + + milestone_events = issue.resourcemilestoneevents.list() + assert isinstance(milestone_events, list) + + milestone_event = issue.resourcemilestoneevents.get(milestone_events[0].id) + assert isinstance( + milestone_event, gitlab.v4.objects.ProjectIssueResourceMilestoneEvent + ) + + milestone_issues = project.issues.list(milestone=milestone.title) + assert len(milestone_issues) == 1 + + +def test_issue_discussions(issue): + size = len(issue.discussions.list()) + + discussion = issue.discussions.create({"body": "Discussion body"}) + assert len(issue.discussions.list()) == size + 1 + + d_note = discussion.notes.create({"body": "first note"}) + d_note_from_get = discussion.notes.get(d_note.id) + d_note_from_get.body = "updated body" + d_note_from_get.save() + + discussion = issue.discussions.get(discussion.id) + 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_merge_requests.py b/tests/functional/api/test_merge_requests.py new file mode 100644 index 0000000..e768234 --- /dev/null +++ b/tests/functional/api/test_merge_requests.py @@ -0,0 +1,165 @@ +import time + +import pytest + +import gitlab +import gitlab.v4.objects + + +def test_merge_requests(project): + project.files.create( + { + "file_path": "README.rst", + "branch": "master", + "content": "Initial content", + "commit_message": "Initial commit", + } + ) + + source_branch = "branch1" + project.branches.create({"branch": source_branch, "ref": "master"}) + + project.files.create( + { + "file_path": "README2.rst", + "branch": source_branch, + "content": "Initial content", + "commit_message": "New commit in new branch", + } + ) + project.mergerequests.create( + {"source_branch": "branch1", "target_branch": "master", "title": "MR readme2"} + ) + + +def test_merge_request_discussion(project): + mr = project.mergerequests.list()[0] + size = len(mr.discussions.list()) + + discussion = mr.discussions.create({"body": "Discussion body"}) + assert len(mr.discussions.list()) == size + 1 + + note = discussion.notes.create({"body": "first note"}) + note_from_get = discussion.notes.get(note.id) + note_from_get.body = "updated body" + note_from_get.save() + + discussion = mr.discussions.get(discussion.id) + 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): + mr = project.mergerequests.list()[0] + mr.labels = ["label2"] + mr.save() + + events = mr.resourcelabelevents.list() + assert events + + event = mr.resourcelabelevents.get(events[0].id) + assert event + + +def test_merge_request_milestone_events(project, milestone): + mr = project.mergerequests.list()[0] + mr.milestone_id = milestone.id + mr.save() + + milestones = mr.resourcemilestoneevents.list() + assert milestones + + milestone = mr.resourcemilestoneevents.get(milestones[0].id) + assert milestone + + +def test_merge_request_basic(project): + mr = project.mergerequests.list()[0] + # basic testing: only make sure that the methods exist + mr.commits() + mr.changes() + assert mr.participants() + + +def test_merge_request_rebase(project): + mr = project.mergerequests.list()[0] + assert mr.rebase() + + +@pytest.mark.skip(reason="flaky test") +def test_merge_request_merge(project): + mr = project.mergerequests.list()[0] + mr.merge() + project.branches.delete(mr.source_branch) + + with pytest.raises(gitlab.GitlabMRClosedError): + # Two merge attempts should raise GitlabMRClosedError + mr.merge() + + +def test_merge_request_should_remove_source_branch( + project, merge_request, wait_for_sidekiq +) -> 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""" + + source_branch = "remove_source_branch" + mr = merge_request(source_branch=source_branch) + + mr.merge(should_remove_source_branch=True) + + result = wait_for_sidekiq(timeout=60) + assert result is True, "sidekiq process should have terminated but did not" + + # Wait until it is merged + mr_iid = mr.iid + for _ in range(60): + mr = project.mergerequests.get(mr_iid) + if mr.merged_at is not None: + break + time.sleep(0.5) + assert mr.merged_at is not None + time.sleep(0.5) + + # Ensure we can NOT get the MR branch + with pytest.raises(gitlab.exceptions.GitlabGetError): + project.branches.get(source_branch) + + +def test_merge_request_large_commit_message( + project, merge_request, wait_for_sidekiq +) -> 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 + cause an error: 414 Request too large + """ + + source_branch = "large_commit_message" + mr = merge_request(source_branch=source_branch) + + merge_commit_message = "large_message\r\n" * 1_000 + assert len(merge_commit_message) > 10_000 + + mr.merge(merge_commit_message=merge_commit_message) + + result = wait_for_sidekiq(timeout=60) + assert result is True, "sidekiq process should have terminated but did not" + + # Wait until it is merged + mr_iid = mr.iid + for _ in range(60): + mr = project.mergerequests.get(mr_iid) + if mr.merged_at is not None: + break + time.sleep(0.5) + assert mr.merged_at is not None + time.sleep(0.5) + + # Ensure we can get the MR branch + project.branches.get(source_branch) diff --git a/tests/functional/api/test_packages.py b/tests/functional/api/test_packages.py new file mode 100644 index 0000000..9160a68 --- /dev/null +++ b/tests/functional/api/test_packages.py @@ -0,0 +1,13 @@ +""" +GitLab API: https://docs.gitlab.com/ce/api/packages.html +""" + + +def test_list_project_packages(project): + packages = project.packages.list() + assert isinstance(packages, list) + + +def test_list_group_packages(group): + packages = group.packages.list() + assert isinstance(packages, list) diff --git a/tests/functional/api/test_projects.py b/tests/functional/api/test_projects.py new file mode 100644 index 0000000..0823c00 --- /dev/null +++ b/tests/functional/api/test_projects.py @@ -0,0 +1,267 @@ +import pytest + +import gitlab + + +def test_create_project(gl, user): + # Moved from group tests chunk in legacy tests, TODO cleanup + admin_project = gl.projects.create({"name": "admin_project"}) + assert isinstance(admin_project, gitlab.v4.objects.Project) + assert len(gl.projects.list(search="admin")) == 1 + + sudo_project = gl.projects.create({"name": "sudo_project"}, sudo=user.id) + + created = gl.projects.list() + created_gen = gl.projects.list(as_list=False) + owned = gl.projects.list(owned=True) + + assert admin_project in created and sudo_project in created + assert admin_project in owned and sudo_project not in owned + assert len(created) == len(list(created_gen)) + + admin_project.delete() + sudo_project.delete() + + +def test_project_badges(project): + badge_image = "http://example.com" + badge_link = "http://example/img.svg" + + badge = project.badges.create({"link_url": badge_link, "image_url": badge_image}) + assert len(project.badges.list()) == 1 + + badge.image_url = "http://another.example.com" + badge.save() + + badge = project.badges.get(badge.id) + assert badge.image_url == "http://another.example.com" + + badge.delete() + assert len(project.badges.list()) == 0 + + +@pytest.mark.skip(reason="Commented out in legacy test") +def test_project_boards(project): + boards = project.boards.list() + assert len(boards) + + board = boards[0] + lists = board.lists.list() + begin_size = len(lists) + last_list = lists[-1] + last_list.position = 0 + last_list.save() + last_list.delete() + lists = board.lists.list() + assert len(lists) == begin_size - 1 + + +def test_project_custom_attributes(gl, project): + attrs = project.customattributes.list() + assert len(attrs) == 0 + + attr = project.customattributes.set("key", "value1") + assert attr.key == "key" + assert attr.value == "value1" + assert len(project.customattributes.list()) == 1 + assert len(gl.projects.list(custom_attributes={"key": "value1"})) == 1 + + attr = project.customattributes.set("key", "value2") + attr = project.customattributes.get("key") + assert attr.value == "value2" + assert len(project.customattributes.list()) == 1 + + attr.delete() + assert len(project.customattributes.list()) == 0 + + +def test_project_environments(project): + project.environments.create( + {"name": "env1", "external_url": "http://fake.env/whatever"} + ) + environments = project.environments.list() + assert len(environments) == 1 + + environment = environments[0] + environment.external_url = "http://new.env/whatever" + environment.save() + + environment = project.environments.list()[0] + assert environment.external_url == "http://new.env/whatever" + + environment.stop() + environment.delete() + assert len(project.environments.list()) == 0 + + +def test_project_events(project): + events = project.events.list() + assert isinstance(events, list) + + +def test_project_file_uploads(project): + filename = "test.txt" + file_contents = "testing contents" + + uploaded_file = project.upload(filename, file_contents) + assert uploaded_file["alt"] == filename + assert uploaded_file["url"].startswith("/uploads/") + assert uploaded_file["url"].endswith("/" + filename) + assert uploaded_file["markdown"] == "[{}]({})".format( + uploaded_file["alt"], uploaded_file["url"] + ) + + +def test_project_forks(gl, project, user): + fork = project.forks.create({"namespace": user.username}) + fork_project = gl.projects.get(fork.id) + assert fork_project.forked_from_project["id"] == project.id + + forks = project.forks.list() + assert fork.id in map(lambda fork_project: fork_project.id, forks) + + +def test_project_hooks(project): + hook = project.hooks.create({"url": "http://hook.url"}) + assert len(project.hooks.list()) == 1 + + hook.note_events = True + hook.save() + + hook = project.hooks.get(hook.id) + assert hook.note_events is True + hook.delete() + + +def test_project_housekeeping(project): + project.housekeeping() + + +def test_project_labels(project): + label = project.labels.create({"name": "label", "color": "#778899"}) + labels = project.labels.list() + assert len(labels) == 1 + + label = project.labels.get("label") + assert label == labels[0] + + label.new_name = "labelupdated" + label.save() + assert label.name == "labelupdated" + + label.subscribe() + assert label.subscribed is True + + label.unsubscribe() + assert label.subscribed is False + + label.delete() + assert len(project.labels.list()) == 0 + + +def test_project_milestones(project): + milestone = project.milestones.create({"title": "milestone1"}) + assert len(project.milestones.list()) == 1 + + milestone.due_date = "2020-01-01T00:00:00Z" + milestone.save() + + milestone.state_event = "close" + milestone.save() + + milestone = project.milestones.get(milestone.id) + assert milestone.state == "closed" + assert len(milestone.issues()) == 0 + assert len(milestone.merge_requests()) == 0 + + +def test_project_pages_domains(gl, project): + domain = project.pagesdomains.create({"domain": "foo.domain.com"}) + assert len(project.pagesdomains.list()) == 1 + assert len(gl.pagesdomains.list()) == 1 + + domain = project.pagesdomains.get("foo.domain.com") + assert domain.domain == "foo.domain.com" + + domain.delete() + assert len(project.pagesdomains.list()) == 0 + + +def test_project_protected_branches(project): + p_b = project.protectedbranches.create({"name": "*-stable"}) + assert p_b.name == "*-stable" + assert len(project.protectedbranches.list()) == 1 + + p_b = project.protectedbranches.get("*-stable") + p_b.delete() + assert len(project.protectedbranches.list()) == 0 + + +def test_project_remote_mirrors(project): + mirror_url = "http://gitlab.test/root/mirror.git" + + mirror = project.remote_mirrors.create({"url": mirror_url}) + assert mirror.url == mirror_url + + mirror.enabled = True + mirror.save() + + mirror = project.remote_mirrors.list()[0] + assert isinstance(mirror, gitlab.v4.objects.ProjectRemoteMirror) + assert mirror.url == mirror_url + assert mirror.enabled is True + + +def test_project_services(project): + service = project.services.get("asana") + service.api_key = "whatever" + service.save() + + service = project.services.get("asana") + assert service.active is True + + service.delete() + + service = project.services.get("asana") + assert service.active is False + + +def test_project_stars(project): + project.star() + assert project.star_count == 1 + + project.unstar() + assert project.star_count == 0 + + +def test_project_tags(project, project_file): + tag = project.tags.create({"tag_name": "v1.0", "ref": "master"}) + assert len(project.tags.list()) == 1 + + tag.set_release_description("Description 1") + tag.set_release_description("Description 2") + assert tag.release["description"] == "Description 2" + + tag.delete() + assert len(project.tags.list()) == 0 + + +def test_project_triggers(project): + trigger = project.triggers.create({"description": "trigger1"}) + assert len(project.triggers.list()) == 1 + trigger.delete() + + +def test_project_wiki(project): + content = "Wiki page content" + wiki = project.wikis.create({"title": "wikipage", "content": content}) + assert len(project.wikis.list()) == 1 + + wiki = project.wikis.get(wiki.slug) + assert wiki.content == content + + # update and delete seem broken + wiki.content = "new content" + wiki.save() + wiki.delete() + assert len(project.wikis.list()) == 0 diff --git a/tests/functional/api/test_releases.py b/tests/functional/api/test_releases.py new file mode 100644 index 0000000..f49181a --- /dev/null +++ b/tests/functional/api/test_releases.py @@ -0,0 +1,36 @@ +release_name = "Demo Release" +release_tag_name = "v1.2.3" +release_description = "release notes go here" + +link_data = {"url": "https://example.com", "name": "link_name"} + + +def test_create_project_release(project, project_file): + project.refresh() # Gets us the current default branch + release = project.releases.create( + { + "name": release_name, + "tag_name": release_tag_name, + "description": release_description, + "ref": project.default_branch, + } + ) + + assert len(project.releases.list()) == 1 + assert project.releases.get(release_tag_name) + assert release.name == release_name + assert release.tag_name == release_tag_name + assert release.description == release_description + + +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): + release.links.create(link_data) + + release = project.releases.get(release.tag_name) + assert release.assets["links"][0]["url"] == link_data["url"] + assert release.assets["links"][0]["name"] == link_data["name"] diff --git a/tests/functional/api/test_repository.py b/tests/functional/api/test_repository.py new file mode 100644 index 0000000..7ba84ea --- /dev/null +++ b/tests/functional/api/test_repository.py @@ -0,0 +1,126 @@ +import base64 +import time + +import pytest + +import gitlab + + +def test_repository_files(project): + project.files.create( + { + "file_path": "README", + "branch": "master", + "content": "Initial content", + "commit_message": "Initial commit", + } + ) + readme = project.files.get(file_path="README", ref="master") + readme.content = base64.b64encode(b"Improved README").decode() + + time.sleep(2) + readme.save(branch="master", commit_message="new commit") + readme.delete(commit_message="Removing README", branch="master") + + project.files.create( + { + "file_path": "README.rst", + "branch": "master", + "content": "Initial content", + "commit_message": "New commit", + } + ) + readme = project.files.get(file_path="README.rst", ref="master") + # The first decode() is the ProjectFile method, the second one is the bytes + # object method + assert readme.decode().decode() == "Initial content" + + blame = project.files.blame(file_path="README.rst", ref="master") + assert blame + + +def test_repository_tree(project): + tree = project.repository_tree() + assert tree + assert tree[0]["name"] == "README.rst" + + blob_id = tree[0]["id"] + blob = project.repository_raw_blob(blob_id) + assert blob.decode() == "Initial content" + + archive = project.repository_archive() + assert isinstance(archive, bytes) + + archive2 = project.repository_archive("master") + assert archive == archive2 + + snapshot = project.snapshot() + assert isinstance(snapshot, bytes) + + +def test_create_commit(project): + data = { + "branch": "master", + "commit_message": "blah blah blah", + "actions": [{"action": "create", "file_path": "blah", "content": "blah"}], + } + commit = project.commits.create(data) + + assert "@@" in project.commits.list()[0].diff()[0]["diff"] + assert isinstance(commit.refs(), list) + assert isinstance(commit.merge_requests(), list) + + +def test_create_commit_status(project): + commit = project.commits.list()[0] + size = len(commit.statuses.list()) + commit.statuses.create({"state": "success", "sha": commit.id}) + assert len(commit.statuses.list()) == size + 1 + + +def test_commit_signature(project): + commit = project.commits.list()[0] + + with pytest.raises(gitlab.GitlabGetError) as e: + commit.signature() + + assert "404 Signature Not Found" in str(e.value) + + +def test_commit_comment(project): + commit = project.commits.list()[0] + + commit.comments.create({"note": "This is a commit comment"}) + assert len(commit.comments.list()) == 1 + + +def test_commit_discussion(project): + commit = project.commits.list()[0] + count = len(commit.discussions.list()) + + discussion = commit.discussions.create({"body": "Discussion body"}) + assert len(commit.discussions.list()) == (count + 1) + + note = discussion.notes.create({"body": "first note"}) + note_from_get = discussion.notes.get(note.id) + 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): + commit = project.commits.list()[0] + revert_commit = commit.revert(branch="master") + + expected_message = 'Revert "{}"\n\nThis reverts commit {}'.format( + commit.message, commit.id + ) + assert revert_commit["message"] == expected_message + + with pytest.raises(gitlab.GitlabRevertError): + # Two revert attempts should raise GitlabRevertError + commit.revert(branch="master") diff --git a/tests/functional/api/test_snippets.py b/tests/functional/api/test_snippets.py new file mode 100644 index 0000000..936fbfb --- /dev/null +++ b/tests/functional/api/test_snippets.py @@ -0,0 +1,74 @@ +import gitlab + + +def test_snippets(gl): + snippets = gl.snippets.list(all=True) + assert len(snippets) == 0 + + snippet = gl.snippets.create( + {"title": "snippet1", "file_name": "snippet1.py", "content": "import gitlab"} + ) + snippet = gl.snippets.get(snippet.id) + snippet.title = "updated_title" + snippet.save() + + snippet = gl.snippets.get(snippet.id) + assert snippet.title == "updated_title" + + content = snippet.content() + assert content.decode() == "import gitlab" + assert snippet.user_agent_detail()["user_agent"] + + snippet.delete() + snippets = gl.snippets.list(all=True) + assert len(snippets) == 0 + + +def test_project_snippets(project): + project.snippets_enabled = True + project.save() + + snippet = project.snippets.create( + { + "title": "snip1", + "file_name": "foo.py", + "content": "initial content", + "visibility": gitlab.v4.objects.VISIBILITY_PRIVATE, + } + ) + + assert snippet.user_agent_detail()["user_agent"] + + +def test_project_snippet_discussion(project): + snippet = project.snippets.list()[0] + size = len(snippet.discussions.list()) + + discussion = snippet.discussions.create({"body": "Discussion body"}) + assert len(snippet.discussions.list()) == size + 1 + + note = discussion.notes.create({"body": "first note"}) + note_from_get = discussion.notes.get(note.id) + note_from_get.body = "updated body" + note_from_get.save() + + discussion = snippet.discussions.get(discussion.id) + 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): + snippet = project.snippets.list()[0] + snippet.file_name = "bar.py" + snippet.save() + + snippet = project.snippets.get(snippet.id) + assert snippet.content().decode() == "initial content" + assert snippet.file_name == "bar.py" + + size = len(project.snippets.list()) + snippet.delete() + assert len(project.snippets.list()) == (size - 1) diff --git a/tests/functional/api/test_users.py b/tests/functional/api/test_users.py new file mode 100644 index 0000000..1ef237c --- /dev/null +++ b/tests/functional/api/test_users.py @@ -0,0 +1,170 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/users.html +https://docs.gitlab.com/ee/api/users.html#delete-authentication-identity-from-user +""" +import pytest +import requests + + +@pytest.fixture(scope="session") +def avatar_path(test_dir): + return test_dir / "fixtures" / "avatar.png" + + +def test_create_user(gl, avatar_path): + user = gl.users.create( + { + "email": "foo@bar.com", + "username": "foo", + "name": "foo", + "password": "foo_password", + "avatar": open(avatar_path, "rb"), + } + ) + + created_user = gl.users.list(username="foo")[0] + assert created_user.username == user.username + assert created_user.email == user.email + + avatar_url = user.avatar_url.replace("gitlab.test", "localhost:8080") + uploaded_avatar = requests.get(avatar_url).content + assert uploaded_avatar == open(avatar_path, "rb").read() + + +def test_block_user(gl, user): + user.block() + users = gl.users.list(blocked=True) + assert user in users + + user.unblock() + users = gl.users.list(blocked=False) + assert user in users + + +def test_delete_user(gl, wait_for_sidekiq): + new_user = gl.users.create( + { + "email": "delete-user@test.com", + "username": "delete-user", + "name": "delete-user", + "password": "delete-user-pass", + } + ) + + new_user.delete() + result = wait_for_sidekiq(timeout=60) + assert result is True, "sidekiq process should have terminated but did not" + + assert new_user.id not in [user.id for user in gl.users.list()] + + +def test_user_projects_list(gl, user): + projects = user.projects.list() + assert isinstance(projects, list) + assert not projects + + +def test_user_events_list(gl, user): + events = user.events.list() + assert isinstance(events, list) + assert not events + + +def test_user_bio(gl, user): + user.bio = "This is the user bio" + user.save() + + +def test_list_multiple_users(gl, user): + second_email = f"{user.email}.2" + second_username = f"{user.username}_2" + second_user = gl.users.create( + { + "email": second_email, + "username": second_username, + "name": "Foo Bar", + "password": "foobar_password", + } + ) + assert gl.users.list(search=second_user.username)[0].id == second_user.id + + expected = [user, second_user] + actual = list(gl.users.list(search=user.username)) + + assert len(expected) == len(actual) + assert len(gl.users.list(search="asdf")) == 0 + + +def test_user_gpg_keys(gl, user, GPG_KEY): + gkey = user.gpgkeys.create({"key": GPG_KEY}) + assert len(user.gpgkeys.list()) == 1 + + # Seems broken on the gitlab side + # gkey = user.gpgkeys.get(gkey.id) + + gkey.delete() + assert len(user.gpgkeys.list()) == 0 + + +def test_user_ssh_keys(gl, user, SSH_KEY): + key = user.keys.create({"title": "testkey", "key": SSH_KEY}) + assert len(user.keys.list()) == 1 + + key.delete() + assert len(user.keys.list()) == 0 + + +def test_user_email(gl, user): + email = user.emails.create({"email": "foo2@bar.com"}) + assert len(user.emails.list()) == 1 + + email.delete() + assert len(user.emails.list()) == 0 + + +def test_user_custom_attributes(gl, user): + attrs = user.customattributes.list() + assert len(attrs) == 0 + + attr = user.customattributes.set("key", "value1") + assert len(gl.users.list(custom_attributes={"key": "value1"})) == 1 + assert attr.key == "key" + assert attr.value == "value1" + assert len(user.customattributes.list()) == 1 + + attr = user.customattributes.set("key", "value2") + attr = user.customattributes.get("key") + assert attr.value == "value2" + assert len(user.customattributes.list()) == 1 + + attr.delete() + assert len(user.customattributes.list()) == 0 + + +def test_user_impersonation_tokens(gl, user): + token = user.impersonationtokens.create( + {"name": "token1", "scopes": ["api", "read_user"]} + ) + + tokens = user.impersonationtokens.list(state="active") + assert len(tokens) == 1 + + token.delete() + tokens = user.impersonationtokens.list(state="active") + assert len(tokens) == 0 + tokens = user.impersonationtokens.list(state="inactive") + assert len(tokens) == 1 + + +def test_user_identities(gl, user): + provider = "test_provider" + + user.provider = provider + user.extern_uid = "1" + user.save() + 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 new file mode 100644 index 0000000..d20ebba --- /dev/null +++ b/tests/functional/api/test_variables.py @@ -0,0 +1,48 @@ +""" +GitLab API: +https://docs.gitlab.com/ee/api/instance_level_ci_variables.html +https://docs.gitlab.com/ee/api/project_level_variables.html +https://docs.gitlab.com/ee/api/group_level_variables.html +""" + + +def test_instance_variables(gl): + variable = gl.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(gl.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = gl.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(gl.variables.list()) == 0 + + +def test_group_variables(group): + variable = group.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(group.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = group.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(group.variables.list()) == 0 + + +def test_project_variables(project): + variable = project.variables.create({"key": "key1", "value": "value1"}) + assert variable.value == "value1" + assert len(project.variables.list()) == 1 + + variable.value = "new_value1" + variable.save() + variable = project.variables.get(variable.key) + assert variable.value == "new_value1" + + variable.delete() + assert len(project.variables.list()) == 0 diff --git a/tests/functional/cli/conftest.py b/tests/functional/cli/conftest.py new file mode 100644 index 0000000..ba94dcb --- /dev/null +++ b/tests/functional/cli/conftest.py @@ -0,0 +1,21 @@ +import pytest + + +@pytest.fixture +def gitlab_cli(script_runner, gitlab_config): + """Wrapper fixture to help make test cases less verbose.""" + + def _gitlab_cli(subcommands): + """ + Return a script_runner.run method that takes a default gitlab + command, and subcommands passed as arguments inside test cases. + """ + command = ["gitlab", "--config-file", gitlab_config] + + for subcommand in subcommands: + # ensure we get strings (e.g from IDs) + command.append(str(subcommand)) + + return script_runner.run(*command) + + return _gitlab_cli diff --git a/tests/functional/cli/test_cli_artifacts.py b/tests/functional/cli/test_cli_artifacts.py new file mode 100644 index 0000000..4cb69aa --- /dev/null +++ b/tests/functional/cli/test_cli_artifacts.py @@ -0,0 +1,51 @@ +import subprocess +import sys +import textwrap +import time +from io import BytesIO +from zipfile import is_zipfile + +import pytest + +content = textwrap.dedent( + """\ + test-artifact: + script: echo "test" > artifact.txt + artifacts: + untracked: true + """ +) +data = { + "file_path": ".gitlab-ci.yml", + "branch": "master", + "content": content, + "commit_message": "Initial commit", +} + + +@pytest.mark.skipif(sys.version_info < (3, 8), reason="I am the walrus") +def test_cli_artifacts(capsysbinary, gitlab_config, gitlab_runner, project): + project.files.create(data) + + while not (jobs := project.jobs.list(scope="success")): + time.sleep(0.5) + + job = project.jobs.get(jobs[0].id) + cmd = [ + "gitlab", + "--config-file", + gitlab_config, + "project-job", + "artifacts", + "--id", + str(job.id), + "--project-id", + str(project.id), + ] + + with capsysbinary.disabled(): + artifacts = subprocess.check_output(cmd) + assert isinstance(artifacts, bytes) + + artifacts_zip = BytesIO(artifacts) + assert is_zipfile(artifacts_zip) diff --git a/tests/functional/cli/test_cli_packages.py b/tests/functional/cli/test_cli_packages.py new file mode 100644 index 0000000..a3734a2 --- /dev/null +++ b/tests/functional/cli/test_cli_packages.py @@ -0,0 +1,12 @@ +def test_list_project_packages(gitlab_cli, project): + cmd = ["project-package", "list", "--project-id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_group_packages(gitlab_cli, group): + cmd = ["group-package", "list", "--group-id", group.id] + ret = gitlab_cli(cmd) + + assert ret.success diff --git a/tests/functional/cli/test_cli_v4.py b/tests/functional/cli/test_cli_v4.py new file mode 100644 index 0000000..a63c1b1 --- /dev/null +++ b/tests/functional/cli/test_cli_v4.py @@ -0,0 +1,715 @@ +import os +import time + + +def test_create_project(gitlab_cli): + name = "test-project1" + + cmd = ["project", "create", "--name", name] + ret = gitlab_cli(cmd) + + assert ret.success + assert name in ret.stdout + + +def test_update_project(gitlab_cli, project): + description = "My New Description" + + cmd = ["project", "update", "--id", project.id, "--description", description] + ret = gitlab_cli(cmd) + + assert ret.success + assert description in ret.stdout + + +def test_create_group(gitlab_cli): + name = "test-group1" + path = "group1" + + cmd = ["group", "create", "--name", name, "--path", path] + ret = gitlab_cli(cmd) + + assert ret.success + assert name in ret.stdout + assert path in ret.stdout + + +def test_update_group(gitlab_cli, gl, group): + description = "My New Description" + + cmd = ["group", "update", "--id", group.id, "--description", description] + ret = gitlab_cli(cmd) + + assert ret.success + + group = gl.groups.get(group.id) + assert group.description == description + + +def test_create_user(gitlab_cli, gl): + email = "fake@email.com" + username = "user1" + name = "User One" + password = "fakepassword" + + cmd = [ + "user", + "create", + "--email", + email, + "--username", + username, + "--name", + name, + "--password", + password, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + user = gl.users.list(username=username)[0] + + assert user.email == email + assert user.username == username + assert user.name == name + + +def test_get_user_by_id(gitlab_cli, user): + cmd = ["user", "get", "--id", user.id] + ret = gitlab_cli(cmd) + + assert ret.success + assert str(user.id) in ret.stdout + + +def test_list_users_verbose_output(gitlab_cli): + cmd = ["-v", "user", "list"] + ret = gitlab_cli(cmd) + + assert ret.success + assert "avatar-url" in ret.stdout + + +def test_cli_args_not_in_output(gitlab_cli): + cmd = ["-v", "user", "list"] + ret = gitlab_cli(cmd) + + assert "config-file" not in ret.stdout + + +def test_add_member_to_project(gitlab_cli, project, user): + access_level = "40" + + cmd = [ + "project-member", + "create", + "--project-id", + project.id, + "--user-id", + user.id, + "--access-level", + access_level, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_user_memberships(gitlab_cli, user): + cmd = ["user-membership", "list", "--user-id", user.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_project_create_file(gitlab_cli, project): + file_path = "README" + branch = "master" + content = "CONTENT" + commit_message = "Initial commit" + + cmd = [ + "project-file", + "create", + "--project-id", + project.id, + "--file-path", + file_path, + "--branch", + branch, + "--content", + content, + "--commit-message", + commit_message, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_project_issue(gitlab_cli, project): + title = "my issue" + description = "my issue description" + + cmd = [ + "project-issue", + "create", + "--project-id", + project.id, + "--title", + title, + "--description", + description, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert title in ret.stdout + + +def test_create_issue_note(gitlab_cli, issue): + body = "body" + + cmd = [ + "project-issue-note", + "create", + "--project-id", + issue.project_id, + "--issue-iid", + issue.iid, + "--body", + body, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_branch(gitlab_cli, project): + branch = "branch1" + + cmd = [ + "project-branch", + "create", + "--project-id", + project.id, + "--branch", + branch, + "--ref", + "master", + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_merge_request(gitlab_cli, project): + branch = "branch1" + + cmd = [ + "project-merge-request", + "create", + "--project-id", + project.id, + "--source-branch", + branch, + "--target-branch", + "master", + "--title", + "Update README", + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +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 = { + "branch": mr.source_branch, + "file_path": "README2", + "content": "Content", + "commit_message": "Pre-merge commit", + } + project.files.create(file_data) + time.sleep(2) + + cmd = [ + "project-merge-request", + "merge", + "--project-id", + project.id, + "--iid", + mr.iid, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_revert_commit(gitlab_cli, project): + commit = project.commits.list()[0] + + cmd = [ + "project-commit", + "revert", + "--project-id", + project.id, + "--id", + commit.id, + "--branch", + "master", + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_get_commit_signature_not_found(gitlab_cli, project): + commit = project.commits.list()[0] + + cmd = ["project-commit", "signature", "--project-id", project.id, "--id", commit.id] + ret = gitlab_cli(cmd) + + assert not ret.success + assert "404 Signature Not Found" in ret.stderr + + +def test_create_project_label(gitlab_cli, project): + name = "prjlabel1" + description = "prjlabel1 description" + color = "#112233" + + cmd = [ + "-v", + "project-label", + "create", + "--project-id", + project.id, + "--name", + name, + "--description", + description, + "--color", + color, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_project_labels(gitlab_cli, project): + cmd = ["-v", "project-label", "list", "--project-id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_update_project_label(gitlab_cli, label): + new_label = "prjlabel2" + new_description = "prjlabel2 description" + new_color = "#332211" + + cmd = [ + "-v", + "project-label", + "update", + "--project-id", + label.project_id, + "--name", + label.name, + "--new-name", + new_label, + "--description", + new_description, + "--color", + new_color, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_delete_project_label(gitlab_cli, label): + # TODO: due to update above, we'd need a function-scope label fixture + label_name = "prjlabel2" + + cmd = [ + "-v", + "project-label", + "delete", + "--project-id", + label.project_id, + "--name", + label_name, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_group_label(gitlab_cli, group): + name = "grouplabel1" + description = "grouplabel1 description" + color = "#112233" + + cmd = [ + "-v", + "group-label", + "create", + "--group-id", + group.id, + "--name", + name, + "--description", + description, + "--color", + color, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_group_labels(gitlab_cli, group): + cmd = ["-v", "group-label", "list", "--group-id", group.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_update_group_label(gitlab_cli, group_label): + new_label = "grouplabel2" + new_description = "grouplabel2 description" + new_color = "#332211" + + cmd = [ + "-v", + "group-label", + "update", + "--group-id", + group_label.group_id, + "--name", + group_label.name, + "--new-name", + new_label, + "--description", + new_description, + "--color", + new_color, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_delete_group_label(gitlab_cli, group_label): + # TODO: due to update above, we'd need a function-scope label fixture + new_label = "grouplabel2" + + cmd = [ + "-v", + "group-label", + "delete", + "--group-id", + group_label.group_id, + "--name", + new_label, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_project_variable(gitlab_cli, project): + key = "junk" + value = "car" + + cmd = [ + "-v", + "project-variable", + "create", + "--project-id", + project.id, + "--key", + key, + "--value", + value, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_get_project_variable(gitlab_cli, variable): + cmd = [ + "-v", + "project-variable", + "get", + "--project-id", + variable.project_id, + "--key", + variable.key, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_update_project_variable(gitlab_cli, variable): + new_value = "bus" + + cmd = [ + "-v", + "project-variable", + "update", + "--project-id", + variable.project_id, + "--key", + variable.key, + "--value", + new_value, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_project_variables(gitlab_cli, project): + cmd = ["-v", "project-variable", "list", "--project-id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_delete_project_variable(gitlab_cli, variable): + cmd = [ + "-v", + "project-variable", + "delete", + "--project-id", + variable.project_id, + "--key", + variable.key, + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +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) + + assert ret.success + + +def test_project_upload_file(gitlab_cli, project): + cmd = [ + "project", + "upload", + "--id", + project.id, + "--filename", + __file__, + "--filepath", + os.path.realpath(__file__), + ] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_get_application_settings(gitlab_cli): + cmd = ["application-settings", "get"] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_update_application_settings(gitlab_cli): + cmd = ["application-settings", "update", "--signup-enabled", "false"] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_create_project_with_values_from_file(gitlab_cli, tmpdir): + name = "gitlab-project-from-file" + description = "Multiline\n\nData\n" + from_file = tmpdir.join(name) + from_file.write(description) + from_file_path = f"@{str(from_file)}" + + cmd = [ + "-v", + "project", + "create", + "--name", + name, + "--description", + from_file_path, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert description in ret.stdout + + +def test_create_project_deploy_token(gitlab_cli, project): + name = "project-token" + username = "root" + expires_at = "2021-09-09" + scopes = "read_registry" + + cmd = [ + "-v", + "project-deploy-token", + "create", + "--project-id", + project.id, + "--name", + name, + "--username", + username, + "--expires-at", + expires_at, + "--scopes", + scopes, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert name in ret.stdout + assert username in ret.stdout + assert expires_at in ret.stdout + assert scopes in ret.stdout + + +def test_list_all_deploy_tokens(gitlab_cli, deploy_token): + cmd = ["-v", "deploy-token", "list"] + ret = gitlab_cli(cmd) + + assert ret.success + assert deploy_token.name in ret.stdout + assert str(deploy_token.id) in ret.stdout + assert deploy_token.username in ret.stdout + assert deploy_token.expires_at in ret.stdout + assert deploy_token.scopes[0] in ret.stdout + + +def test_list_project_deploy_tokens(gitlab_cli, deploy_token): + cmd = [ + "-v", + "project-deploy-token", + "list", + "--project-id", + deploy_token.project_id, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert deploy_token.name in ret.stdout + assert str(deploy_token.id) in ret.stdout + assert deploy_token.username in ret.stdout + assert deploy_token.expires_at in ret.stdout + assert deploy_token.scopes[0] in ret.stdout + + +def test_delete_project_deploy_token(gitlab_cli, deploy_token): + cmd = [ + "-v", + "project-deploy-token", + "delete", + "--project-id", + deploy_token.project_id, + "--id", + deploy_token.id, + ] + ret = gitlab_cli(cmd) + + assert ret.success + # TODO assert not in list + + +def test_create_group_deploy_token(gitlab_cli, group): + name = "group-token" + username = "root" + expires_at = "2021-09-09" + scopes = "read_registry" + + cmd = [ + "-v", + "group-deploy-token", + "create", + "--group-id", + group.id, + "--name", + name, + "--username", + username, + "--expires-at", + expires_at, + "--scopes", + scopes, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert name in ret.stdout + assert username in ret.stdout + assert expires_at in ret.stdout + assert scopes in ret.stdout + + +def test_list_group_deploy_tokens(gitlab_cli, group_deploy_token): + cmd = [ + "-v", + "group-deploy-token", + "list", + "--group-id", + group_deploy_token.group_id, + ] + ret = gitlab_cli(cmd) + + assert ret.success + assert group_deploy_token.name in ret.stdout + assert str(group_deploy_token.id) in ret.stdout + assert group_deploy_token.username in ret.stdout + assert group_deploy_token.expires_at in ret.stdout + assert group_deploy_token.scopes[0] in ret.stdout + + +def test_delete_group_deploy_token(gitlab_cli, group_deploy_token): + cmd = [ + "-v", + "group-deploy-token", + "delete", + "--group-id", + group_deploy_token.group_id, + "--id", + group_deploy_token.id, + ] + ret = gitlab_cli(cmd) + + assert ret.success + # TODO assert not in list + + +def test_delete_project(gitlab_cli, project): + cmd = ["project", "delete", "--id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_delete_group(gitlab_cli, group): + cmd = ["group", "delete", "--id", group.id] + ret = gitlab_cli(cmd) + + assert ret.success diff --git a/tests/functional/cli/test_cli_variables.py b/tests/functional/cli/test_cli_variables.py new file mode 100644 index 0000000..9b1b16d --- /dev/null +++ b/tests/functional/cli/test_cli_variables.py @@ -0,0 +1,19 @@ +def test_list_instance_variables(gitlab_cli, gl): + cmd = ["variable", "list"] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_group_variables(gitlab_cli, group): + cmd = ["group-variable", "list", "--group-id", group.id] + ret = gitlab_cli(cmd) + + assert ret.success + + +def test_list_project_variables(gitlab_cli, project): + cmd = ["project-variable", "list", "--project-id", project.id] + ret = gitlab_cli(cmd) + + assert ret.success diff --git a/tests/functional/conftest.py b/tests/functional/conftest.py new file mode 100644 index 0000000..5d3b1b9 --- /dev/null +++ b/tests/functional/conftest.py @@ -0,0 +1,462 @@ +import tempfile +import time +import uuid +from pathlib import Path +from subprocess import check_output + +import pytest + +import gitlab + + +def reset_gitlab(gl): + # previously tools/reset_gitlab.py + for project in gl.projects.list(): + project.delete() + for group in gl.groups.list(): + group.delete() + for variable in gl.variables.list(): + variable.delete() + for user in gl.users.list(): + if user.username != "root": + user.delete() + + +def set_token(container, rootdir): + set_token_rb = rootdir / "fixtures" / "set_token.rb" + + with open(set_token_rb, "r") as f: + set_token_command = f.read().strip() + + rails_command = [ + "docker", + "exec", + container, + "gitlab-rails", + "runner", + set_token_command, + ] + output = check_output(rails_command).decode().strip() + + return output + + +def pytest_report_collectionfinish(config, startdir, items): + return [ + "", + "Starting GitLab container.", + "Waiting for GitLab to reconfigure.", + "This may take a few minutes.", + ] + + +@pytest.fixture(scope="session") +def temp_dir(): + return Path(tempfile.gettempdir()) + + +@pytest.fixture(scope="session") +def test_dir(pytestconfig): + return pytestconfig.rootdir / "tests" / "functional" + + +@pytest.fixture(scope="session") +def docker_compose_file(test_dir): + return test_dir / "fixtures" / "docker-compose.yml" + + +@pytest.fixture(scope="session") +def check_is_alive(): + """ + Return a healthcheck function fixture for the GitLab container spinup. + """ + + def _check(container): + logs = ["docker", "logs", container] + return "gitlab Reconfigured!" in check_output(logs).decode() + + return _check + + +@pytest.fixture +def wait_for_sidekiq(gl): + """ + Return a helper function to wait until there are no busy sidekiq processes. + + Use this with asserts for slow tasks (group/project/user creation/deletion). + """ + + def _wait(timeout=30, step=0.5): + for _ in range(timeout): + time.sleep(step) + busy = False + processes = gl.sidekiq.process_metrics()["processes"] + for process in processes: + if process["busy"]: + busy = True + if not busy: + return True + return False + + return _wait + + +@pytest.fixture(scope="session") +def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, test_dir): + config_file = temp_dir / "python-gitlab.cfg" + port = docker_services.port_for("gitlab", 80) + + docker_services.wait_until_responsive( + timeout=200, pause=5, check=lambda: check_is_alive("gitlab-test") + ) + + token = set_token("gitlab-test", rootdir=test_dir) + + config = f"""[global] +default = local +timeout = 60 + +[local] +url = http://{docker_ip}:{port} +private_token = {token} +api_version = 4""" + + with open(config_file, "w") as f: + f.write(config) + + return config_file + + +@pytest.fixture(scope="session") +def gl(gitlab_config): + """Helper instance to make fixtures and asserts directly via the API.""" + + instance = gitlab.Gitlab.from_config("local", [gitlab_config]) + reset_gitlab(instance) + + return instance + + +@pytest.fixture(scope="session") +def gitlab_runner(gl): + container = "gitlab-runner-test" + runner_name = "python-gitlab-runner" + token = "registration-token" + url = "http://gitlab" + + docker_exec = ["docker", "exec", container, "gitlab-runner"] + register = [ + "register", + "--run-untagged", + "--non-interactive", + "--registration-token", + token, + "--name", + runner_name, + "--url", + url, + "--clone-url", + url, + "--executor", + "shell", + ] + unregister = ["unregister", "--name", runner_name] + + yield check_output(docker_exec + register).decode() + + check_output(docker_exec + unregister).decode() + + +@pytest.fixture(scope="module") +def group(gl): + """Group fixture for group API resource tests.""" + _id = uuid.uuid4().hex + data = { + "name": f"test-group-{_id}", + "path": f"group-{_id}", + } + group = gl.groups.create(data) + + yield group + + try: + group.delete() + except gitlab.exceptions.GitlabDeleteError as e: + print(f"Group already deleted: {e}") + + +@pytest.fixture(scope="module") +def project(gl): + """Project fixture for project API resource tests.""" + _id = uuid.uuid4().hex + name = f"test-project-{_id}" + + project = gl.projects.create(name=name) + + yield project + + try: + project.delete() + except gitlab.exceptions.GitlabDeleteError as e: + print(f"Project already deleted: {e}") + + +@pytest.fixture(scope="function") +def merge_request(project, wait_for_sidekiq): + """Fixture used to create a merge_request. + + It will create a branch, add a commit to the branch, and then create a + merge request against project.default_branch. The MR will be returned. + + When finished any created merge requests and branches will be deleted. + + NOTE: No attempt is made to restore project.default_branch to its previous + state. So if the merge request is merged then its content will be in the + project.default_branch branch. + """ + + to_delete = [] + + def _merge_request(*, source_branch: str): + # Wait for processes to be done before we start... + # 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" + + project.refresh() # Gets us the current default branch + project.branches.create( + {"branch": source_branch, "ref": project.default_branch} + ) + # NOTE(jlvillal): Must create a commit in the new branch before we can + # create an MR that will work. + project.files.create( + { + "file_path": f"README.{source_branch}", + "branch": source_branch, + "content": "Initial content", + "commit_message": "New commit in new branch", + } + ) + mr = project.mergerequests.create( + { + "source_branch": source_branch, + "target_branch": project.default_branch, + "title": "Should remove source branch", + "remove_source_branch": True, + } + ) + result = wait_for_sidekiq(timeout=60) + assert result is True, "sidekiq process should have terminated but did not" + + mr_iid = mr.iid + for _ in range(60): + mr = project.mergerequests.get(mr_iid) + if mr.merge_status != "checking": + break + time.sleep(0.5) + assert mr.merge_status != "checking" + + to_delete.append((mr.iid, source_branch)) + return mr + + yield _merge_request + + for mr_iid, source_branch in to_delete: + project.mergerequests.delete(mr_iid) + try: + project.branches.delete(source_branch) + except gitlab.exceptions.GitlabDeleteError: + # Ignore if branch was already deleted + pass + + +@pytest.fixture(scope="module") +def project_file(project): + """File fixture for tests requiring a project with files and branches.""" + project_file = project.files.create( + { + "file_path": "README", + "branch": "master", + "content": "Initial content", + "commit_message": "Initial commit", + } + ) + + return project_file + + +@pytest.fixture(scope="function") +def release(project, project_file): + _id = uuid.uuid4().hex + name = f"test-release-{_id}" + + project.refresh() # Gets us the current default branch + release = project.releases.create( + { + "name": name, + "tag_name": _id, + "description": "description", + "ref": project.default_branch, + } + ) + + return release + + +@pytest.fixture(scope="module") +def user(gl): + """User fixture for user API resource tests.""" + _id = uuid.uuid4().hex + email = f"user{_id}@email.com" + username = f"user{_id}" + name = f"User {_id}" + password = "fakepassword" + + user = gl.users.create(email=email, username=username, name=name, password=password) + + yield user + + try: + user.delete() + except gitlab.exceptions.GitlabDeleteError as e: + print(f"User already deleted: {e}") + + +@pytest.fixture(scope="module") +def issue(project): + """Issue fixture for issue API resource tests.""" + _id = uuid.uuid4().hex + data = {"title": f"Issue {_id}", "description": f"Issue {_id} description"} + + return project.issues.create(data) + + +@pytest.fixture(scope="module") +def milestone(project): + _id = uuid.uuid4().hex + data = {"title": f"milestone{_id}"} + + return project.milestones.create(data) + + +@pytest.fixture(scope="module") +def label(project): + """Label fixture for project label API resource tests.""" + _id = uuid.uuid4().hex + data = { + "name": f"prjlabel{_id}", + "description": f"prjlabel1 {_id} description", + "color": "#112233", + } + + return project.labels.create(data) + + +@pytest.fixture(scope="module") +def group_label(group): + """Label fixture for group label API resource tests.""" + _id = uuid.uuid4().hex + data = { + "name": f"grplabel{_id}", + "description": f"grplabel1 {_id} description", + "color": "#112233", + } + + return group.labels.create(data) + + +@pytest.fixture(scope="module") +def variable(project): + """Variable fixture for project variable API resource tests.""" + _id = uuid.uuid4().hex + data = {"key": f"var{_id}", "value": f"Variable {_id}"} + + return project.variables.create(data) + + +@pytest.fixture(scope="module") +def deploy_token(project): + """Deploy token fixture for project deploy token API resource tests.""" + _id = uuid.uuid4().hex + data = { + "name": f"token-{_id}", + "username": "root", + "expires_at": "2021-09-09", + "scopes": "read_registry", + } + + return project.deploytokens.create(data) + + +@pytest.fixture(scope="module") +def group_deploy_token(group): + """Deploy token fixture for group deploy token API resource tests.""" + _id = uuid.uuid4().hex + data = { + "name": f"group-token-{_id}", + "username": "root", + "expires_at": "2021-09-09", + "scopes": "read_registry", + } + + return group.deploytokens.create(data) + + +@pytest.fixture(scope="session") +def GPG_KEY(): + return """-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBFn5mzYBCADH6SDVPAp1zh/hxmTi0QplkOfExBACpuY6OhzNdIg+8/528b3g +Y5YFR6T/HLv/PmeHskUj21end1C0PNG2T9dTx+2Vlh9ISsSG1kyF9T5fvMR3bE0x +Dl6S489CXZrjPTS9SHk1kF+7dwjUxLJyxF9hPiSihFefDFu3NeOtG/u8vbC1mewQ +ZyAYue+mqtqcCIFFoBz7wHKMWjIVSJSyTkXExu4OzpVvy3l2EikbvavI3qNz84b+ +Mgkv/kiBlNoCy3CVuPk99RYKZ3lX1vVtqQ0OgNGQvb4DjcpyjmbKyibuZwhDjIOh +au6d1OyEbayTntd+dQ4j9EMSnEvm/0MJ4eXPABEBAAG0G0dpdGxhYlRlc3QxIDxm +YWtlQGZha2UudGxkPokBNwQTAQgAIQUCWfmbNgIbAwULCQgHAgYVCAkKCwIEFgID +AQIeAQIXgAAKCRBgxELHf8f3hF3yB/wNJlWPKY65UsB4Lo0hs1OxdxCDqXogSi0u +6crDEIiyOte62pNZKzWy8TJcGZvznRTZ7t8hXgKFLz3PRMcl+vAiRC6quIDUj+2V +eYfwaItd1lUfzvdCaC7Venf4TQ74f5vvNg/zoGwE6eRoSbjlLv9nqsxeA0rUBUQL +LYikWhVMP3TrlfgfduYvh6mfgh57BDLJ9kJVpyfxxx9YLKZbaas9sPa6LgBtR555 +JziUxHmbEv8XCsUU8uoFeP1pImbNBplqE3wzJwzOMSmmch7iZzrAwfN7N2j3Wj0H +B5kQddJ9dmB4BbU0IXGhWczvdpxboI2wdY8a1JypxOdePoph/43iuQENBFn5mzYB +CADnTPY0Zf3d9zLjBNgIb3yDl94uOcKCq0twNmyjMhHzGqw+UMe9BScy34GL94Al +xFRQoaL+7P8hGsnsNku29A/VDZivcI+uxTx4WQ7OLcn7V0bnHV4d76iky2ufbUt/ +GofthjDs1SonePO2N09sS4V4uK0d5N4BfCzzXgvg8etCLxNmC9BGt7AaKUUzKBO4 +2QvNNaC2C/8XEnOgNWYvR36ylAXAmo0sGFXUsBCTiq1fugS9pwtaS2JmaVpZZ3YT +pMZlS0+SjC5BZYFqSmKCsA58oBRzCxQz57nR4h5VEflgD+Hy0HdW0UHETwz83E6/ +U0LL6YyvhwFr6KPq5GxinSvfABEBAAGJAR8EGAEIAAkFAln5mzYCGwwACgkQYMRC +x3/H94SJgwgAlKQb10/xcL/epdDkR7vbiei7huGLBpRDb/L5fM8B5W77Qi8Xmuqj +cCu1j99ZCA5hs/vwVn8j8iLSBGMC5gxcuaar/wtmiaEvT9fO/h6q4opG7NcuiJ8H +wRj8ccJmRssNqDD913PLz7T40Ts62blhrEAlJozGVG/q7T3RAZcskOUHKeHfc2RI +YzGsC/I9d7k6uxAv1L9Nm5F2HaAQDzhkdd16nKkGaPGR35cT1JLInkfl5cdm7ldN +nxs4TLO3kZjUTgWKdhpgRNF5hwaz51ZjpebaRf/ZqRuNyX4lIRolDxzOn/+O1o8L +qG2ZdhHHmSK2LaQLFiSprUkikStNU9BqSQ== +=5OGa +-----END PGP PUBLIC KEY BLOCK-----""" + + +@pytest.fixture(scope="session") +def SSH_KEY(): + return ( + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih" + "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n" + "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l" + "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI" + "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh" + "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar" + ) + + +@pytest.fixture(scope="session") +def DEPLOY_KEY(): + return ( + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG" + "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI" + "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6" + "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu" + "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv" + "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc" + "vn bar@foo" + ) diff --git a/tests/functional/ee-test.py b/tests/functional/ee-test.py new file mode 100755 index 0000000..3a99951 --- /dev/null +++ b/tests/functional/ee-test.py @@ -0,0 +1,158 @@ +#!/usr/bin/env python + +import gitlab + +P1 = "root/project1" +P2 = "root/project2" +MR_P1 = 1 +I_P1 = 1 +I_P2 = 1 +EPIC_ISSUES = [4, 5] +G1 = "group1" +LDAP_CN = "app1" +LDAP_PROVIDER = "ldapmain" + + +def start_log(message): + print("Testing %s... " % message, end="") + + +def end_log(): + print("OK") + + +gl = gitlab.Gitlab.from_config("ee") +project1 = gl.projects.get(P1) +project2 = gl.projects.get(P2) +issue_p1 = project1.issues.get(I_P1) +issue_p2 = project2.issues.get(I_P2) +group1 = gl.groups.get(G1) +mr = project1.mergerequests.get(1) + +start_log("MR approvals") +approval = project1.approvals.get() +v = approval.reset_approvals_on_push +approval.reset_approvals_on_push = not v +approval.save() +approval = project1.approvals.get() +assert v != approval.reset_approvals_on_push +project1.approvals.set_approvers(1, [1], []) +approval = project1.approvals.get() +assert approval.approvers[0]["user"]["id"] == 1 + +approval = mr.approvals.get() +approval.approvals_required = 2 +approval.save() +approval = mr.approvals.get() +assert approval.approvals_required == 2 +approval.approvals_required = 3 +approval.save() +approval = mr.approvals.get() +assert approval.approvals_required == 3 +mr.approvals.set_approvers(1, [1], []) +approval = mr.approvals.get() +assert approval.approvers[0]["user"]["id"] == 1 + +ars = project1.approvalrules.list(all=True) +assert len(ars) == 0 +project1.approvalrules.create( + {"name": "approval-rule", "approvals_required": 1, "group_ids": [group1.id]} +) +ars = project1.approvalrules.list(all=True) +assert len(ars) == 1 +assert ars[0].approvals_required == 2 +ars[0].save() +ars = project1.approvalrules.list(all=True) +assert len(ars) == 1 +assert ars[0].approvals_required == 2 +ars[0].delete() +ars = project1.approvalrules.list(all=True) +assert len(ars) == 0 +end_log() + +start_log("geo nodes") +# very basic tests because we only have 1 node... +nodes = gl.geonodes.list() +status = gl.geonodes.status() +end_log() + +start_log("issue links") +# bit of cleanup just in case +for link in issue_p1.links.list(): + issue_p1.links.delete(link.issue_link_id) + +src, dst = issue_p1.links.create({"target_project_id": P2, "target_issue_iid": I_P2}) +links = issue_p1.links.list() +link_id = links[0].issue_link_id +issue_p1.links.delete(link_id) +end_log() + +start_log("LDAP links") +# bit of cleanup just in case +if hasattr(group1, "ldap_group_links"): + for link in group1.ldap_group_links: + group1.delete_ldap_group_link(link["cn"], link["provider"]) +assert gl.ldapgroups.list() +group1.add_ldap_group_link(LDAP_CN, 30, LDAP_PROVIDER) +group1.ldap_sync() +group1.delete_ldap_group_link(LDAP_CN) +end_log() + +start_log("boards") +# bit of cleanup just in case +for board in project1.boards.list(): + if board.name == "testboard": + board.delete() +board = project1.boards.create({"name": "testboard"}) +board = project1.boards.get(board.id) +project1.boards.delete(board.id) + +for board in group1.boards.list(): + if board.name == "testboard": + board.delete() +board = group1.boards.create({"name": "testboard"}) +board = group1.boards.get(board.id) +group1.boards.delete(board.id) +end_log() + +start_log("push rules") +pr = project1.pushrules.get() +if pr: + pr.delete() +pr = project1.pushrules.create({"deny_delete_tag": True}) +pr.deny_delete_tag = False +pr.save() +pr = project1.pushrules.get() +assert pr is not None +assert pr.deny_delete_tag is False +pr.delete() +end_log() + +start_log("license") +license = gl.get_license() +assert "user_limit" in license +try: + gl.set_license("dummykey") +except Exception as e: + assert "The license key is invalid." in e.error_message +end_log() + +start_log("epics") +epic = group1.epics.create({"title": "Test epic"}) +epic.title = "Fixed title" +epic.labels = ["label1", "label2"] +epic.save() +epic = group1.epics.get(epic.iid) +assert epic.title == "Fixed title" +assert len(group1.epics.list()) + +# issues +assert not epic.issues.list() +for i in EPIC_ISSUES: + epic.issues.create({"issue_id": i}) +assert len(EPIC_ISSUES) == len(epic.issues.list()) +for ei in epic.issues.list(): + ei.delete() + +epic.delete() +end_log() diff --git a/tests/functional/fixtures/.env b/tests/functional/fixtures/.env new file mode 100644 index 0000000..eacfb28 --- /dev/null +++ b/tests/functional/fixtures/.env @@ -0,0 +1,2 @@ +GITLAB_IMAGE=gitlab/gitlab-ce +GITLAB_TAG=13.11.4-ce.0 diff --git a/tests/functional/fixtures/avatar.png b/tests/functional/fixtures/avatar.png new file mode 100644 index 0000000..a3a767c Binary files /dev/null and b/tests/functional/fixtures/avatar.png differ diff --git a/tests/functional/fixtures/docker-compose.yml b/tests/functional/fixtures/docker-compose.yml new file mode 100644 index 0000000..a0794d6 --- /dev/null +++ b/tests/functional/fixtures/docker-compose.yml @@ -0,0 +1,46 @@ +version: '3' + +networks: + gitlab-network: + name: gitlab-network + +services: + gitlab: + image: '${GITLAB_IMAGE}:${GITLAB_TAG}' + container_name: 'gitlab-test' + hostname: 'gitlab.test' + privileged: true # Just in case https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/1350 + environment: + GITLAB_OMNIBUS_CONFIG: | + external_url 'http://gitlab.test' + gitlab_rails['initial_root_password'] = '5iveL!fe' + gitlab_rails['initial_shared_runners_registration_token'] = 'registration-token' + registry['enable'] = false + nginx['redirect_http_to_https'] = false + nginx['listen_port'] = 80 + nginx['listen_https'] = false + pages_external_url 'http://pages.gitlab.lxd' + gitlab_pages['enable'] = true + gitlab_pages['inplace_chroot'] = true + prometheus['enable'] = false + alertmanager['enable'] = false + node_exporter['enable'] = false + redis_exporter['enable'] = false + postgres_exporter['enable'] = false + pgbouncer_exporter['enable'] = false + gitlab_exporter['enable'] = false + grafana['enable'] = false + letsencrypt['enable'] = false + ports: + - '8080:80' + - '2222:22' + networks: + - gitlab-network + + gitlab-runner: + image: gitlab/gitlab-runner:latest + container_name: 'gitlab-runner-test' + depends_on: + - gitlab + networks: + - gitlab-network diff --git a/tests/functional/fixtures/set_token.rb b/tests/functional/fixtures/set_token.rb new file mode 100644 index 0000000..735dcd5 --- /dev/null +++ b/tests/functional/fixtures/set_token.rb @@ -0,0 +1,9 @@ +# https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#programmatically-creating-a-personal-access-token + +user = User.find_by_username('root') + +token = user.personal_access_tokens.create(scopes: [:api, :sudo], name: 'default'); +token.set_token('python-gitlab-token'); +token.save! + +puts token.token diff --git a/tools/functional/api/test_clusters.py b/tools/functional/api/test_clusters.py deleted file mode 100644 index 8930aad..0000000 --- a/tools/functional/api/test_clusters.py +++ /dev/null @@ -1,46 +0,0 @@ -def test_project_clusters(project): - project.clusters.create( - { - "name": "cluster1", - "platform_kubernetes_attributes": { - "api_url": "http://url", - "token": "tokenval", - }, - } - ) - clusters = project.clusters.list() - assert len(clusters) == 1 - - cluster = clusters[0] - cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} - cluster.save() - - cluster = project.clusters.list()[0] - assert cluster.platform_kubernetes["api_url"] == "http://newurl" - - cluster.delete() - assert len(project.clusters.list()) == 0 - - -def test_group_clusters(group): - group.clusters.create( - { - "name": "cluster1", - "platform_kubernetes_attributes": { - "api_url": "http://url", - "token": "tokenval", - }, - } - ) - clusters = group.clusters.list() - assert len(clusters) == 1 - - cluster = clusters[0] - cluster.platform_kubernetes_attributes = {"api_url": "http://newurl"} - cluster.save() - - cluster = group.clusters.list()[0] - assert cluster.platform_kubernetes["api_url"] == "http://newurl" - - cluster.delete() - assert len(group.clusters.list()) == 0 diff --git a/tools/functional/api/test_current_user.py b/tools/functional/api/test_current_user.py deleted file mode 100644 index 5802457..0000000 --- a/tools/functional/api/test_current_user.py +++ /dev/null @@ -1,42 +0,0 @@ -def test_current_user_email(gl): - gl.auth() - mail = gl.user.emails.create({"email": "current@user.com"}) - assert len(gl.user.emails.list()) == 1 - - mail.delete() - assert len(gl.user.emails.list()) == 0 - - -def test_current_user_gpg_keys(gl, GPG_KEY): - gl.auth() - gkey = gl.user.gpgkeys.create({"key": GPG_KEY}) - assert len(gl.user.gpgkeys.list()) == 1 - - # Seems broken on the gitlab side - gkey = gl.user.gpgkeys.get(gkey.id) - gkey.delete() - assert len(gl.user.gpgkeys.list()) == 0 - - -def test_current_user_ssh_keys(gl, SSH_KEY): - gl.auth() - key = gl.user.keys.create({"title": "testkey", "key": SSH_KEY}) - assert len(gl.user.keys.list()) == 1 - - key.delete() - assert len(gl.user.keys.list()) == 0 - - -def test_current_user_status(gl): - gl.auth() - message = "Test" - emoji = "thumbsup" - status = gl.user.status.get() - - status.message = message - status.emoji = emoji - status.save() - - new_status = gl.user.status.get() - assert new_status.message == message - assert new_status.emoji == emoji diff --git a/tools/functional/api/test_deploy_keys.py b/tools/functional/api/test_deploy_keys.py deleted file mode 100644 index 18828a2..0000000 --- a/tools/functional/api/test_deploy_keys.py +++ /dev/null @@ -1,12 +0,0 @@ -def test_project_deploy_keys(gl, project, DEPLOY_KEY): - deploy_key = project.keys.create({"title": "foo@bar", "key": DEPLOY_KEY}) - project_keys = list(project.keys.list()) - assert len(project_keys) == 1 - - project2 = gl.projects.create({"name": "deploy-key-project"}) - project2.keys.enable(deploy_key.id) - assert len(project2.keys.list()) == 1 - - project2.keys.delete(deploy_key.id) - assert len(project2.keys.list()) == 0 - project2.delete() diff --git a/tools/functional/api/test_deploy_tokens.py b/tools/functional/api/test_deploy_tokens.py deleted file mode 100644 index efcf8b1..0000000 --- a/tools/functional/api/test_deploy_tokens.py +++ /dev/null @@ -1,36 +0,0 @@ -def test_project_deploy_tokens(gl, project): - deploy_token = project.deploytokens.create( - { - "name": "foo", - "username": "bar", - "expires_at": "2022-01-01", - "scopes": ["read_registry"], - } - ) - assert len(project.deploytokens.list()) == 1 - assert gl.deploytokens.list() == project.deploytokens.list() - - assert project.deploytokens.list()[0].name == "foo" - assert project.deploytokens.list()[0].expires_at == "2022-01-01T00:00:00.000Z" - assert project.deploytokens.list()[0].scopes == ["read_registry"] - assert project.deploytokens.list()[0].username == "bar" - - deploy_token.delete() - assert len(project.deploytokens.list()) == 0 - assert len(gl.deploytokens.list()) == 0 - - -def test_group_deploy_tokens(gl, group): - deploy_token = group.deploytokens.create( - { - "name": "foo", - "scopes": ["read_registry"], - } - ) - - assert len(group.deploytokens.list()) == 1 - assert gl.deploytokens.list() == group.deploytokens.list() - - deploy_token.delete() - assert len(group.deploytokens.list()) == 0 - assert len(gl.deploytokens.list()) == 0 diff --git a/tools/functional/api/test_gitlab.py b/tools/functional/api/test_gitlab.py deleted file mode 100644 index 7a70a56..0000000 --- a/tools/functional/api/test_gitlab.py +++ /dev/null @@ -1,183 +0,0 @@ -import pytest - -import gitlab - - -def test_auth_from_config(gl, temp_dir): - """Test token authentication from config file""" - test_gitlab = gitlab.Gitlab.from_config( - config_files=[temp_dir / "python-gitlab.cfg"] - ) - test_gitlab.auth() - assert isinstance(test_gitlab.user, gitlab.v4.objects.CurrentUser) - - -def test_broadcast_messages(gl): - msg = gl.broadcastmessages.create({"message": "this is the message"}) - msg.color = "#444444" - msg.save() - msg_id = msg.id - - msg = gl.broadcastmessages.list(all=True)[0] - assert msg.color == "#444444" - - msg = gl.broadcastmessages.get(msg_id) - assert msg.color == "#444444" - - msg.delete() - assert len(gl.broadcastmessages.list()) == 0 - - -def test_markdown(gl): - html = gl.markdown("foo") - assert "foo" in html - - -def test_lint(gl): - success, errors = gl.lint("Invalid") - assert success is False - assert errors - - -def test_sidekiq_queue_metrics(gl): - out = gl.sidekiq.queue_metrics() - assert isinstance(out, dict) - assert "pages" in out["queues"] - - -def test_sidekiq_process_metrics(gl): - out = gl.sidekiq.process_metrics() - assert isinstance(out, dict) - assert "hostname" in out["processes"][0] - - -def test_sidekiq_job_stats(gl): - out = gl.sidekiq.job_stats() - assert isinstance(out, dict) - assert "processed" in out["jobs"] - - -def test_sidekiq_compound_metrics(gl): - out = gl.sidekiq.compound_metrics() - assert isinstance(out, dict) - assert "jobs" in out - assert "processes" in out - assert "queues" in out - - -def test_gitlab_settings(gl): - settings = gl.settings.get() - settings.default_projects_limit = 42 - settings.save() - settings = gl.settings.get() - assert settings.default_projects_limit == 42 - - -def test_template_dockerfile(gl): - assert gl.dockerfiles.list() - - dockerfile = gl.dockerfiles.get("Node") - assert dockerfile.content is not None - - -def test_template_gitignore(gl): - assert gl.gitignores.list() - gitignore = gl.gitignores.get("Node") - assert gitignore.content is not None - - -def test_template_gitlabciyml(gl): - assert gl.gitlabciymls.list() - gitlabciyml = gl.gitlabciymls.get("Nodejs") - assert gitlabciyml.content is not None - - -def test_template_license(gl): - assert gl.licenses.list() - license = gl.licenses.get( - "bsd-2-clause", project="mytestproject", fullname="mytestfullname" - ) - assert "mytestfullname" in license.content - - -def test_hooks(gl): - hook = gl.hooks.create({"url": "http://whatever.com"}) - assert len(gl.hooks.list()) == 1 - - hook.delete() - assert len(gl.hooks.list()) == 0 - - -def test_namespaces(gl): - namespace = gl.namespaces.list(all=True) - assert namespace - - namespace = gl.namespaces.list(search="root", all=True)[0] - assert namespace.kind == "user" - - -def test_notification_settings(gl): - settings = gl.notificationsettings.get() - settings.level = gitlab.NOTIFICATION_LEVEL_WATCH - settings.save() - - settings = gl.notificationsettings.get() - assert settings.level == gitlab.NOTIFICATION_LEVEL_WATCH - - -def test_user_activities(gl): - activities = gl.user_activities.list(query_parameters={"from": "2019-01-01"}) - assert isinstance(activities, list) - - -def test_events(gl): - events = gl.events.list() - assert isinstance(events, list) - - -@pytest.mark.skip -def test_features(gl): - feat = gl.features.set("foo", 30) - assert feat.name == "foo" - assert len(gl.features.list()) == 1 - - feat.delete() - assert len(gl.features.list()) == 0 - - -def test_pagination(gl, project): - project2 = gl.projects.create({"name": "project-page-2"}) - - list1 = gl.projects.list(per_page=1, page=1) - list2 = gl.projects.list(per_page=1, page=2) - assert len(list1) == 1 - assert len(list2) == 1 - assert list1[0].id != list2[0].id - - project2.delete() - - -def test_rate_limits(gl): - settings = gl.settings.get() - settings.throttle_authenticated_api_enabled = True - settings.throttle_authenticated_api_requests_per_period = 1 - settings.throttle_authenticated_api_period_in_seconds = 3 - settings.save() - - projects = list() - for i in range(0, 20): - projects.append(gl.projects.create({"name": str(i) + "ok"})) - - with pytest.raises(gitlab.GitlabCreateError) as e: - for i in range(20, 40): - projects.append( - gl.projects.create( - {"name": str(i) + "shouldfail"}, obey_rate_limit=False - ) - ) - - assert "Retry later" in str(e.value) - - settings.throttle_authenticated_api_enabled = False - settings.save() - [project.delete() for project in projects] diff --git a/tools/functional/api/test_groups.py b/tools/functional/api/test_groups.py deleted file mode 100644 index eae2d9b..0000000 --- a/tools/functional/api/test_groups.py +++ /dev/null @@ -1,195 +0,0 @@ -import pytest - -import gitlab - - -def test_groups(gl): - # TODO: This one still needs lots of work - user = gl.users.create( - { - "email": "user@test.com", - "username": "user", - "name": "user", - "password": "user_pass", - } - ) - user2 = gl.users.create( - { - "email": "user2@test.com", - "username": "user2", - "name": "user2", - "password": "user2_pass", - } - ) - group1 = gl.groups.create({"name": "group1", "path": "group1"}) - group2 = gl.groups.create({"name": "group2", "path": "group2"}) - - p_id = gl.groups.list(search="group2")[0].id - group3 = gl.groups.create({"name": "group3", "path": "group3", "parent_id": p_id}) - group4 = gl.groups.create({"name": "group4", "path": "group4"}) - - assert len(gl.groups.list()) == 4 - assert len(gl.groups.list(search="oup1")) == 1 - assert group3.parent_id == p_id - assert group2.subgroups.list()[0].id == group3.id - - filtered_groups = gl.groups.list(skip_groups=[group3.id, group4.id]) - assert group3 not in filtered_groups - assert group3 not in filtered_groups - - group1.members.create( - {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user.id} - ) - group1.members.create( - {"access_level": gitlab.const.GUEST_ACCESS, "user_id": user2.id} - ) - group2.members.create( - {"access_level": gitlab.const.OWNER_ACCESS, "user_id": user2.id} - ) - - group4.share(group1.id, gitlab.const.DEVELOPER_ACCESS) - group4.share(group2.id, gitlab.const.MAINTAINER_ACCESS) - # Reload group4 to have updated shared_with_groups - group4 = gl.groups.get(group4.id) - assert len(group4.shared_with_groups) == 2 - group4.unshare(group1.id) - # Reload group4 to have updated shared_with_groups - group4 = gl.groups.get(group4.id) - assert len(group4.shared_with_groups) == 1 - - # User memberships (admin only) - memberships1 = user.memberships.list() - assert len(memberships1) == 1 - - memberships2 = user2.memberships.list() - assert len(memberships2) == 2 - - membership = memberships1[0] - assert membership.source_type == "Namespace" - assert membership.access_level == gitlab.const.OWNER_ACCESS - - project_memberships = user.memberships.list(type="Project") - assert len(project_memberships) == 0 - - group_memberships = user.memberships.list(type="Namespace") - assert len(group_memberships) == 1 - - with pytest.raises(gitlab.GitlabListError) as e: - membership = user.memberships.list(type="Invalid") - assert "type does not have a valid value" in str(e.value) - - with pytest.raises(gitlab.GitlabListError) as e: - user.memberships.list(sudo=user.name) - assert "403 Forbidden" in str(e.value) - - # Administrator belongs to the groups - assert len(group1.members.list()) == 3 - assert len(group2.members.list()) == 2 - - group1.members.delete(user.id) - assert len(group1.members.list()) == 2 - assert len(group1.members.all()) # Deprecated - assert len(group1.members_all.list()) - member = group1.members.get(user2.id) - member.access_level = gitlab.const.OWNER_ACCESS - member.save() - member = group1.members.get(user2.id) - assert member.access_level == gitlab.const.OWNER_ACCESS - - group2.members.delete(gl.user.id) - - -@pytest.mark.skip(reason="Commented out in legacy test") -def test_group_labels(group): - group.labels.create({"name": "foo", "description": "bar", "color": "#112233"}) - label = group.labels.get("foo") - assert label.description == "bar" - - label.description = "baz" - label.save() - label = group.labels.get("foo") - assert label.description == "baz" - assert len(group.labels.list()) == 1 - - label.delete() - assert len(group.labels.list()) == 0 - - -def test_group_notification_settings(group): - settings = group.notificationsettings.get() - settings.level = "disabled" - settings.save() - - settings = group.notificationsettings.get() - assert settings.level == "disabled" - - -def test_group_badges(group): - badge_image = "http://example.com" - badge_link = "http://example/img.svg" - badge = group.badges.create({"link_url": badge_link, "image_url": badge_image}) - assert len(group.badges.list()) == 1 - - badge.image_url = "http://another.example.com" - badge.save() - - badge = group.badges.get(badge.id) - assert badge.image_url == "http://another.example.com" - - badge.delete() - assert len(group.badges.list()) == 0 - - -def test_group_milestones(group): - milestone = group.milestones.create({"title": "groupmilestone1"}) - assert len(group.milestones.list()) == 1 - - milestone.due_date = "2020-01-01T00:00:00Z" - milestone.save() - milestone.state_event = "close" - milestone.save() - - milestone = group.milestones.get(milestone.id) - assert milestone.state == "closed" - assert len(milestone.issues()) == 0 - assert len(milestone.merge_requests()) == 0 - - -def test_group_custom_attributes(gl, group): - attrs = group.customattributes.list() - assert len(attrs) == 0 - - attr = group.customattributes.set("key", "value1") - assert len(gl.groups.list(custom_attributes={"key": "value1"})) == 1 - assert attr.key == "key" - assert attr.value == "value1" - assert len(group.customattributes.list()) == 1 - - attr = group.customattributes.set("key", "value2") - attr = group.customattributes.get("key") - assert attr.value == "value2" - assert len(group.customattributes.list()) == 1 - - attr.delete() - assert len(group.customattributes.list()) == 0 - - -def test_group_subgroups_projects(gl, user): - # TODO: fixture factories - group1 = gl.groups.list(search="group1")[0] - group2 = gl.groups.list(search="group2")[0] - - group3 = gl.groups.create( - {"name": "subgroup1", "path": "subgroup1", "parent_id": group1.id} - ) - group4 = gl.groups.create( - {"name": "subgroup2", "path": "subgroup2", "parent_id": group2.id} - ) - - gr1_project = gl.projects.create({"name": "gr1_project", "namespace_id": group1.id}) - gr2_project = gl.projects.create({"name": "gr2_project", "namespace_id": group3.id}) - - assert group3.parent_id == group1.id - assert group4.parent_id == group2.id - assert gr1_project.namespace["id"] == group1.id - assert gr2_project.namespace["parent_id"] == group1.id diff --git a/tools/functional/api/test_import_export.py b/tools/functional/api/test_import_export.py deleted file mode 100644 index d4bdd19..0000000 --- a/tools/functional/api/test_import_export.py +++ /dev/null @@ -1,66 +0,0 @@ -import time - -import gitlab - - -def test_group_import_export(gl, group, temp_dir): - export = group.exports.create() - assert export.message == "202 Accepted" - - # We cannot check for export_status with group export API - time.sleep(10) - - import_archive = temp_dir / "gitlab-group-export.tgz" - import_path = "imported_group" - import_name = "Imported Group" - - with open(import_archive, "wb") as f: - export.download(streamed=True, action=f.write) - - with open(import_archive, "rb") as f: - output = gl.groups.import_group(f, import_path, import_name) - assert output["message"] == "202 Accepted" - - # We cannot check for returned ID with group import API - time.sleep(10) - group_import = gl.groups.get(import_path) - - assert group_import.path == import_path - assert group_import.name == import_name - - -def test_project_import_export(gl, project, temp_dir): - export = project.exports.create() - assert export.message == "202 Accepted" - - export = project.exports.get() - assert isinstance(export, gitlab.v4.objects.ProjectExport) - - count = 0 - while export.export_status != "finished": - time.sleep(1) - export.refresh() - count += 1 - if count == 15: - raise Exception("Project export taking too much time") - - with open(temp_dir / "gitlab-export.tgz", "wb") as f: - export.download(streamed=True, action=f.write) - - output = gl.projects.import_project( - open(temp_dir / "gitlab-export.tgz", "rb"), - "imported_project", - name="Imported Project", - ) - project_import = gl.projects.get(output["id"], lazy=True).imports.get() - - assert project_import.path == "imported_project" - assert project_import.name == "Imported Project" - - count = 0 - while project_import.import_status != "finished": - time.sleep(1) - project_import.refresh() - count += 1 - if count == 15: - raise Exception("Project import taking too much time") diff --git a/tools/functional/api/test_issues.py b/tools/functional/api/test_issues.py deleted file mode 100644 index f3a606b..0000000 --- a/tools/functional/api/test_issues.py +++ /dev/null @@ -1,93 +0,0 @@ -import gitlab - - -def test_create_issue(project): - issue = project.issues.create({"title": "my issue 1"}) - issue2 = project.issues.create({"title": "my issue 2"}) - issue_ids = [issue.id for issue in project.issues.list()] - assert len(issue_ids) == 2 - - # Test 'iids' as a list - assert len(project.issues.list(iids=issue_ids)) == 2 - - issue2.state_event = "close" - issue2.save() - assert len(project.issues.list(state="closed")) == 1 - assert len(project.issues.list(state="opened")) == 1 - - 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 - - -def test_issue_notes(issue): - size = len(issue.notes.list()) - - note = issue.notes.create({"body": "This is an issue note"}) - assert len(issue.notes.list()) == size + 1 - - emoji = note.awardemojis.create({"name": "tractor"}) - assert len(note.awardemojis.list()) == 1 - - emoji.delete() - assert len(note.awardemojis.list()) == 0 - - note.delete() - assert len(issue.notes.list()) == size - - -def test_issue_labels(project, issue): - project.labels.create({"name": "label2", "color": "#aabbcc"}) - issue.labels = ["label2"] - issue.save() - - assert issue in project.issues.list(labels=["label2"]) - assert issue in project.issues.list(labels="label2") - assert issue in project.issues.list(labels="Any") - assert issue not in project.issues.list(labels="None") - - -def test_issue_events(issue): - events = issue.resourcelabelevents.list() - assert isinstance(events, list) - - event = issue.resourcelabelevents.get(events[0].id) - assert isinstance(event, gitlab.v4.objects.ProjectIssueResourceLabelEvent) - - -def test_issue_milestones(project, milestone): - data = {"title": "my issue 1", "milestone_id": milestone.id} - issue = project.issues.create(data) - assert milestone.issues().next().title == "my issue 1" - - milestone_events = issue.resourcemilestoneevents.list() - assert isinstance(milestone_events, list) - - milestone_event = issue.resourcemilestoneevents.get(milestone_events[0].id) - assert isinstance( - milestone_event, gitlab.v4.objects.ProjectIssueResourceMilestoneEvent - ) - - milestone_issues = project.issues.list(milestone=milestone.title) - assert len(milestone_issues) == 1 - - -def test_issue_discussions(issue): - size = len(issue.discussions.list()) - - discussion = issue.discussions.create({"body": "Discussion body"}) - assert len(issue.discussions.list()) == size + 1 - - d_note = discussion.notes.create({"body": "first note"}) - d_note_from_get = discussion.notes.get(d_note.id) - d_note_from_get.body = "updated body" - d_note_from_get.save() - - discussion = issue.discussions.get(discussion.id) - 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/tools/functional/api/test_merge_requests.py b/tools/functional/api/test_merge_requests.py deleted file mode 100644 index e768234..0000000 --- a/tools/functional/api/test_merge_requests.py +++ /dev/null @@ -1,165 +0,0 @@ -import time - -import pytest - -import gitlab -import gitlab.v4.objects - - -def test_merge_requests(project): - project.files.create( - { - "file_path": "README.rst", - "branch": "master", - "content": "Initial content", - "commit_message": "Initial commit", - } - ) - - source_branch = "branch1" - project.branches.create({"branch": source_branch, "ref": "master"}) - - project.files.create( - { - "file_path": "README2.rst", - "branch": source_branch, - "content": "Initial content", - "commit_message": "New commit in new branch", - } - ) - project.mergerequests.create( - {"source_branch": "branch1", "target_branch": "master", "title": "MR readme2"} - ) - - -def test_merge_request_discussion(project): - mr = project.mergerequests.list()[0] - size = len(mr.discussions.list()) - - discussion = mr.discussions.create({"body": "Discussion body"}) - assert len(mr.discussions.list()) == size + 1 - - note = discussion.notes.create({"body": "first note"}) - note_from_get = discussion.notes.get(note.id) - note_from_get.body = "updated body" - note_from_get.save() - - discussion = mr.discussions.get(discussion.id) - 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): - mr = project.mergerequests.list()[0] - mr.labels = ["label2"] - mr.save() - - events = mr.resourcelabelevents.list() - assert events - - event = mr.resourcelabelevents.get(events[0].id) - assert event - - -def test_merge_request_milestone_events(project, milestone): - mr = project.mergerequests.list()[0] - mr.milestone_id = milestone.id - mr.save() - - milestones = mr.resourcemilestoneevents.list() - assert milestones - - milestone = mr.resourcemilestoneevents.get(milestones[0].id) - assert milestone - - -def test_merge_request_basic(project): - mr = project.mergerequests.list()[0] - # basic testing: only make sure that the methods exist - mr.commits() - mr.changes() - assert mr.participants() - - -def test_merge_request_rebase(project): - mr = project.mergerequests.list()[0] - assert mr.rebase() - - -@pytest.mark.skip(reason="flaky test") -def test_merge_request_merge(project): - mr = project.mergerequests.list()[0] - mr.merge() - project.branches.delete(mr.source_branch) - - with pytest.raises(gitlab.GitlabMRClosedError): - # Two merge attempts should raise GitlabMRClosedError - mr.merge() - - -def test_merge_request_should_remove_source_branch( - project, merge_request, wait_for_sidekiq -) -> 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""" - - source_branch = "remove_source_branch" - mr = merge_request(source_branch=source_branch) - - mr.merge(should_remove_source_branch=True) - - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" - - # Wait until it is merged - mr_iid = mr.iid - for _ in range(60): - mr = project.mergerequests.get(mr_iid) - if mr.merged_at is not None: - break - time.sleep(0.5) - assert mr.merged_at is not None - time.sleep(0.5) - - # Ensure we can NOT get the MR branch - with pytest.raises(gitlab.exceptions.GitlabGetError): - project.branches.get(source_branch) - - -def test_merge_request_large_commit_message( - project, merge_request, wait_for_sidekiq -) -> 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 - cause an error: 414 Request too large - """ - - source_branch = "large_commit_message" - mr = merge_request(source_branch=source_branch) - - merge_commit_message = "large_message\r\n" * 1_000 - assert len(merge_commit_message) > 10_000 - - mr.merge(merge_commit_message=merge_commit_message) - - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" - - # Wait until it is merged - mr_iid = mr.iid - for _ in range(60): - mr = project.mergerequests.get(mr_iid) - if mr.merged_at is not None: - break - time.sleep(0.5) - assert mr.merged_at is not None - time.sleep(0.5) - - # Ensure we can get the MR branch - project.branches.get(source_branch) diff --git a/tools/functional/api/test_packages.py b/tools/functional/api/test_packages.py deleted file mode 100644 index 9160a68..0000000 --- a/tools/functional/api/test_packages.py +++ /dev/null @@ -1,13 +0,0 @@ -""" -GitLab API: https://docs.gitlab.com/ce/api/packages.html -""" - - -def test_list_project_packages(project): - packages = project.packages.list() - assert isinstance(packages, list) - - -def test_list_group_packages(group): - packages = group.packages.list() - assert isinstance(packages, list) diff --git a/tools/functional/api/test_projects.py b/tools/functional/api/test_projects.py deleted file mode 100644 index 0823c00..0000000 --- a/tools/functional/api/test_projects.py +++ /dev/null @@ -1,267 +0,0 @@ -import pytest - -import gitlab - - -def test_create_project(gl, user): - # Moved from group tests chunk in legacy tests, TODO cleanup - admin_project = gl.projects.create({"name": "admin_project"}) - assert isinstance(admin_project, gitlab.v4.objects.Project) - assert len(gl.projects.list(search="admin")) == 1 - - sudo_project = gl.projects.create({"name": "sudo_project"}, sudo=user.id) - - created = gl.projects.list() - created_gen = gl.projects.list(as_list=False) - owned = gl.projects.list(owned=True) - - assert admin_project in created and sudo_project in created - assert admin_project in owned and sudo_project not in owned - assert len(created) == len(list(created_gen)) - - admin_project.delete() - sudo_project.delete() - - -def test_project_badges(project): - badge_image = "http://example.com" - badge_link = "http://example/img.svg" - - badge = project.badges.create({"link_url": badge_link, "image_url": badge_image}) - assert len(project.badges.list()) == 1 - - badge.image_url = "http://another.example.com" - badge.save() - - badge = project.badges.get(badge.id) - assert badge.image_url == "http://another.example.com" - - badge.delete() - assert len(project.badges.list()) == 0 - - -@pytest.mark.skip(reason="Commented out in legacy test") -def test_project_boards(project): - boards = project.boards.list() - assert len(boards) - - board = boards[0] - lists = board.lists.list() - begin_size = len(lists) - last_list = lists[-1] - last_list.position = 0 - last_list.save() - last_list.delete() - lists = board.lists.list() - assert len(lists) == begin_size - 1 - - -def test_project_custom_attributes(gl, project): - attrs = project.customattributes.list() - assert len(attrs) == 0 - - attr = project.customattributes.set("key", "value1") - assert attr.key == "key" - assert attr.value == "value1" - assert len(project.customattributes.list()) == 1 - assert len(gl.projects.list(custom_attributes={"key": "value1"})) == 1 - - attr = project.customattributes.set("key", "value2") - attr = project.customattributes.get("key") - assert attr.value == "value2" - assert len(project.customattributes.list()) == 1 - - attr.delete() - assert len(project.customattributes.list()) == 0 - - -def test_project_environments(project): - project.environments.create( - {"name": "env1", "external_url": "http://fake.env/whatever"} - ) - environments = project.environments.list() - assert len(environments) == 1 - - environment = environments[0] - environment.external_url = "http://new.env/whatever" - environment.save() - - environment = project.environments.list()[0] - assert environment.external_url == "http://new.env/whatever" - - environment.stop() - environment.delete() - assert len(project.environments.list()) == 0 - - -def test_project_events(project): - events = project.events.list() - assert isinstance(events, list) - - -def test_project_file_uploads(project): - filename = "test.txt" - file_contents = "testing contents" - - uploaded_file = project.upload(filename, file_contents) - assert uploaded_file["alt"] == filename - assert uploaded_file["url"].startswith("/uploads/") - assert uploaded_file["url"].endswith("/" + filename) - assert uploaded_file["markdown"] == "[{}]({})".format( - uploaded_file["alt"], uploaded_file["url"] - ) - - -def test_project_forks(gl, project, user): - fork = project.forks.create({"namespace": user.username}) - fork_project = gl.projects.get(fork.id) - assert fork_project.forked_from_project["id"] == project.id - - forks = project.forks.list() - assert fork.id in map(lambda fork_project: fork_project.id, forks) - - -def test_project_hooks(project): - hook = project.hooks.create({"url": "http://hook.url"}) - assert len(project.hooks.list()) == 1 - - hook.note_events = True - hook.save() - - hook = project.hooks.get(hook.id) - assert hook.note_events is True - hook.delete() - - -def test_project_housekeeping(project): - project.housekeeping() - - -def test_project_labels(project): - label = project.labels.create({"name": "label", "color": "#778899"}) - labels = project.labels.list() - assert len(labels) == 1 - - label = project.labels.get("label") - assert label == labels[0] - - label.new_name = "labelupdated" - label.save() - assert label.name == "labelupdated" - - label.subscribe() - assert label.subscribed is True - - label.unsubscribe() - assert label.subscribed is False - - label.delete() - assert len(project.labels.list()) == 0 - - -def test_project_milestones(project): - milestone = project.milestones.create({"title": "milestone1"}) - assert len(project.milestones.list()) == 1 - - milestone.due_date = "2020-01-01T00:00:00Z" - milestone.save() - - milestone.state_event = "close" - milestone.save() - - milestone = project.milestones.get(milestone.id) - assert milestone.state == "closed" - assert len(milestone.issues()) == 0 - assert len(milestone.merge_requests()) == 0 - - -def test_project_pages_domains(gl, project): - domain = project.pagesdomains.create({"domain": "foo.domain.com"}) - assert len(project.pagesdomains.list()) == 1 - assert len(gl.pagesdomains.list()) == 1 - - domain = project.pagesdomains.get("foo.domain.com") - assert domain.domain == "foo.domain.com" - - domain.delete() - assert len(project.pagesdomains.list()) == 0 - - -def test_project_protected_branches(project): - p_b = project.protectedbranches.create({"name": "*-stable"}) - assert p_b.name == "*-stable" - assert len(project.protectedbranches.list()) == 1 - - p_b = project.protectedbranches.get("*-stable") - p_b.delete() - assert len(project.protectedbranches.list()) == 0 - - -def test_project_remote_mirrors(project): - mirror_url = "http://gitlab.test/root/mirror.git" - - mirror = project.remote_mirrors.create({"url": mirror_url}) - assert mirror.url == mirror_url - - mirror.enabled = True - mirror.save() - - mirror = project.remote_mirrors.list()[0] - assert isinstance(mirror, gitlab.v4.objects.ProjectRemoteMirror) - assert mirror.url == mirror_url - assert mirror.enabled is True - - -def test_project_services(project): - service = project.services.get("asana") - service.api_key = "whatever" - service.save() - - service = project.services.get("asana") - assert service.active is True - - service.delete() - - service = project.services.get("asana") - assert service.active is False - - -def test_project_stars(project): - project.star() - assert project.star_count == 1 - - project.unstar() - assert project.star_count == 0 - - -def test_project_tags(project, project_file): - tag = project.tags.create({"tag_name": "v1.0", "ref": "master"}) - assert len(project.tags.list()) == 1 - - tag.set_release_description("Description 1") - tag.set_release_description("Description 2") - assert tag.release["description"] == "Description 2" - - tag.delete() - assert len(project.tags.list()) == 0 - - -def test_project_triggers(project): - trigger = project.triggers.create({"description": "trigger1"}) - assert len(project.triggers.list()) == 1 - trigger.delete() - - -def test_project_wiki(project): - content = "Wiki page content" - wiki = project.wikis.create({"title": "wikipage", "content": content}) - assert len(project.wikis.list()) == 1 - - wiki = project.wikis.get(wiki.slug) - assert wiki.content == content - - # update and delete seem broken - wiki.content = "new content" - wiki.save() - wiki.delete() - assert len(project.wikis.list()) == 0 diff --git a/tools/functional/api/test_releases.py b/tools/functional/api/test_releases.py deleted file mode 100644 index f49181a..0000000 --- a/tools/functional/api/test_releases.py +++ /dev/null @@ -1,36 +0,0 @@ -release_name = "Demo Release" -release_tag_name = "v1.2.3" -release_description = "release notes go here" - -link_data = {"url": "https://example.com", "name": "link_name"} - - -def test_create_project_release(project, project_file): - project.refresh() # Gets us the current default branch - release = project.releases.create( - { - "name": release_name, - "tag_name": release_tag_name, - "description": release_description, - "ref": project.default_branch, - } - ) - - assert len(project.releases.list()) == 1 - assert project.releases.get(release_tag_name) - assert release.name == release_name - assert release.tag_name == release_tag_name - assert release.description == release_description - - -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): - release.links.create(link_data) - - release = project.releases.get(release.tag_name) - assert release.assets["links"][0]["url"] == link_data["url"] - assert release.assets["links"][0]["name"] == link_data["name"] diff --git a/tools/functional/api/test_repository.py b/tools/functional/api/test_repository.py deleted file mode 100644 index 7ba84ea..0000000 --- a/tools/functional/api/test_repository.py +++ /dev/null @@ -1,126 +0,0 @@ -import base64 -import time - -import pytest - -import gitlab - - -def test_repository_files(project): - project.files.create( - { - "file_path": "README", - "branch": "master", - "content": "Initial content", - "commit_message": "Initial commit", - } - ) - readme = project.files.get(file_path="README", ref="master") - readme.content = base64.b64encode(b"Improved README").decode() - - time.sleep(2) - readme.save(branch="master", commit_message="new commit") - readme.delete(commit_message="Removing README", branch="master") - - project.files.create( - { - "file_path": "README.rst", - "branch": "master", - "content": "Initial content", - "commit_message": "New commit", - } - ) - readme = project.files.get(file_path="README.rst", ref="master") - # The first decode() is the ProjectFile method, the second one is the bytes - # object method - assert readme.decode().decode() == "Initial content" - - blame = project.files.blame(file_path="README.rst", ref="master") - assert blame - - -def test_repository_tree(project): - tree = project.repository_tree() - assert tree - assert tree[0]["name"] == "README.rst" - - blob_id = tree[0]["id"] - blob = project.repository_raw_blob(blob_id) - assert blob.decode() == "Initial content" - - archive = project.repository_archive() - assert isinstance(archive, bytes) - - archive2 = project.repository_archive("master") - assert archive == archive2 - - snapshot = project.snapshot() - assert isinstance(snapshot, bytes) - - -def test_create_commit(project): - data = { - "branch": "master", - "commit_message": "blah blah blah", - "actions": [{"action": "create", "file_path": "blah", "content": "blah"}], - } - commit = project.commits.create(data) - - assert "@@" in project.commits.list()[0].diff()[0]["diff"] - assert isinstance(commit.refs(), list) - assert isinstance(commit.merge_requests(), list) - - -def test_create_commit_status(project): - commit = project.commits.list()[0] - size = len(commit.statuses.list()) - commit.statuses.create({"state": "success", "sha": commit.id}) - assert len(commit.statuses.list()) == size + 1 - - -def test_commit_signature(project): - commit = project.commits.list()[0] - - with pytest.raises(gitlab.GitlabGetError) as e: - commit.signature() - - assert "404 Signature Not Found" in str(e.value) - - -def test_commit_comment(project): - commit = project.commits.list()[0] - - commit.comments.create({"note": "This is a commit comment"}) - assert len(commit.comments.list()) == 1 - - -def test_commit_discussion(project): - commit = project.commits.list()[0] - count = len(commit.discussions.list()) - - discussion = commit.discussions.create({"body": "Discussion body"}) - assert len(commit.discussions.list()) == (count + 1) - - note = discussion.notes.create({"body": "first note"}) - note_from_get = discussion.notes.get(note.id) - 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): - commit = project.commits.list()[0] - revert_commit = commit.revert(branch="master") - - expected_message = 'Revert "{}"\n\nThis reverts commit {}'.format( - commit.message, commit.id - ) - assert revert_commit["message"] == expected_message - - with pytest.raises(gitlab.GitlabRevertError): - # Two revert attempts should raise GitlabRevertError - commit.revert(branch="master") diff --git a/tools/functional/api/test_snippets.py b/tools/functional/api/test_snippets.py deleted file mode 100644 index 936fbfb..0000000 --- a/tools/functional/api/test_snippets.py +++ /dev/null @@ -1,74 +0,0 @@ -import gitlab - - -def test_snippets(gl): - snippets = gl.snippets.list(all=True) - assert len(snippets) == 0 - - snippet = gl.snippets.create( - {"title": "snippet1", "file_name": "snippet1.py", "content": "import gitlab"} - ) - snippet = gl.snippets.get(snippet.id) - snippet.title = "updated_title" - snippet.save() - - snippet = gl.snippets.get(snippet.id) - assert snippet.title == "updated_title" - - content = snippet.content() - assert content.decode() == "import gitlab" - assert snippet.user_agent_detail()["user_agent"] - - snippet.delete() - snippets = gl.snippets.list(all=True) - assert len(snippets) == 0 - - -def test_project_snippets(project): - project.snippets_enabled = True - project.save() - - snippet = project.snippets.create( - { - "title": "snip1", - "file_name": "foo.py", - "content": "initial content", - "visibility": gitlab.v4.objects.VISIBILITY_PRIVATE, - } - ) - - assert snippet.user_agent_detail()["user_agent"] - - -def test_project_snippet_discussion(project): - snippet = project.snippets.list()[0] - size = len(snippet.discussions.list()) - - discussion = snippet.discussions.create({"body": "Discussion body"}) - assert len(snippet.discussions.list()) == size + 1 - - note = discussion.notes.create({"body": "first note"}) - note_from_get = discussion.notes.get(note.id) - note_from_get.body = "updated body" - note_from_get.save() - - discussion = snippet.discussions.get(discussion.id) - 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): - snippet = project.snippets.list()[0] - snippet.file_name = "bar.py" - snippet.save() - - snippet = project.snippets.get(snippet.id) - assert snippet.content().decode() == "initial content" - assert snippet.file_name == "bar.py" - - size = len(project.snippets.list()) - snippet.delete() - assert len(project.snippets.list()) == (size - 1) diff --git a/tools/functional/api/test_users.py b/tools/functional/api/test_users.py deleted file mode 100644 index 1ef237c..0000000 --- a/tools/functional/api/test_users.py +++ /dev/null @@ -1,170 +0,0 @@ -""" -GitLab API: -https://docs.gitlab.com/ee/api/users.html -https://docs.gitlab.com/ee/api/users.html#delete-authentication-identity-from-user -""" -import pytest -import requests - - -@pytest.fixture(scope="session") -def avatar_path(test_dir): - return test_dir / "fixtures" / "avatar.png" - - -def test_create_user(gl, avatar_path): - user = gl.users.create( - { - "email": "foo@bar.com", - "username": "foo", - "name": "foo", - "password": "foo_password", - "avatar": open(avatar_path, "rb"), - } - ) - - created_user = gl.users.list(username="foo")[0] - assert created_user.username == user.username - assert created_user.email == user.email - - avatar_url = user.avatar_url.replace("gitlab.test", "localhost:8080") - uploaded_avatar = requests.get(avatar_url).content - assert uploaded_avatar == open(avatar_path, "rb").read() - - -def test_block_user(gl, user): - user.block() - users = gl.users.list(blocked=True) - assert user in users - - user.unblock() - users = gl.users.list(blocked=False) - assert user in users - - -def test_delete_user(gl, wait_for_sidekiq): - new_user = gl.users.create( - { - "email": "delete-user@test.com", - "username": "delete-user", - "name": "delete-user", - "password": "delete-user-pass", - } - ) - - new_user.delete() - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" - - assert new_user.id not in [user.id for user in gl.users.list()] - - -def test_user_projects_list(gl, user): - projects = user.projects.list() - assert isinstance(projects, list) - assert not projects - - -def test_user_events_list(gl, user): - events = user.events.list() - assert isinstance(events, list) - assert not events - - -def test_user_bio(gl, user): - user.bio = "This is the user bio" - user.save() - - -def test_list_multiple_users(gl, user): - second_email = f"{user.email}.2" - second_username = f"{user.username}_2" - second_user = gl.users.create( - { - "email": second_email, - "username": second_username, - "name": "Foo Bar", - "password": "foobar_password", - } - ) - assert gl.users.list(search=second_user.username)[0].id == second_user.id - - expected = [user, second_user] - actual = list(gl.users.list(search=user.username)) - - assert len(expected) == len(actual) - assert len(gl.users.list(search="asdf")) == 0 - - -def test_user_gpg_keys(gl, user, GPG_KEY): - gkey = user.gpgkeys.create({"key": GPG_KEY}) - assert len(user.gpgkeys.list()) == 1 - - # Seems broken on the gitlab side - # gkey = user.gpgkeys.get(gkey.id) - - gkey.delete() - assert len(user.gpgkeys.list()) == 0 - - -def test_user_ssh_keys(gl, user, SSH_KEY): - key = user.keys.create({"title": "testkey", "key": SSH_KEY}) - assert len(user.keys.list()) == 1 - - key.delete() - assert len(user.keys.list()) == 0 - - -def test_user_email(gl, user): - email = user.emails.create({"email": "foo2@bar.com"}) - assert len(user.emails.list()) == 1 - - email.delete() - assert len(user.emails.list()) == 0 - - -def test_user_custom_attributes(gl, user): - attrs = user.customattributes.list() - assert len(attrs) == 0 - - attr = user.customattributes.set("key", "value1") - assert len(gl.users.list(custom_attributes={"key": "value1"})) == 1 - assert attr.key == "key" - assert attr.value == "value1" - assert len(user.customattributes.list()) == 1 - - attr = user.customattributes.set("key", "value2") - attr = user.customattributes.get("key") - assert attr.value == "value2" - assert len(user.customattributes.list()) == 1 - - attr.delete() - assert len(user.customattributes.list()) == 0 - - -def test_user_impersonation_tokens(gl, user): - token = user.impersonationtokens.create( - {"name": "token1", "scopes": ["api", "read_user"]} - ) - - tokens = user.impersonationtokens.list(state="active") - assert len(tokens) == 1 - - token.delete() - tokens = user.impersonationtokens.list(state="active") - assert len(tokens) == 0 - tokens = user.impersonationtokens.list(state="inactive") - assert len(tokens) == 1 - - -def test_user_identities(gl, user): - provider = "test_provider" - - user.provider = provider - user.extern_uid = "1" - user.save() - 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/tools/functional/api/test_variables.py b/tools/functional/api/test_variables.py deleted file mode 100644 index d20ebba..0000000 --- a/tools/functional/api/test_variables.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -GitLab API: -https://docs.gitlab.com/ee/api/instance_level_ci_variables.html -https://docs.gitlab.com/ee/api/project_level_variables.html -https://docs.gitlab.com/ee/api/group_level_variables.html -""" - - -def test_instance_variables(gl): - variable = gl.variables.create({"key": "key1", "value": "value1"}) - assert variable.value == "value1" - assert len(gl.variables.list()) == 1 - - variable.value = "new_value1" - variable.save() - variable = gl.variables.get(variable.key) - assert variable.value == "new_value1" - - variable.delete() - assert len(gl.variables.list()) == 0 - - -def test_group_variables(group): - variable = group.variables.create({"key": "key1", "value": "value1"}) - assert variable.value == "value1" - assert len(group.variables.list()) == 1 - - variable.value = "new_value1" - variable.save() - variable = group.variables.get(variable.key) - assert variable.value == "new_value1" - - variable.delete() - assert len(group.variables.list()) == 0 - - -def test_project_variables(project): - variable = project.variables.create({"key": "key1", "value": "value1"}) - assert variable.value == "value1" - assert len(project.variables.list()) == 1 - - variable.value = "new_value1" - variable.save() - variable = project.variables.get(variable.key) - assert variable.value == "new_value1" - - variable.delete() - assert len(project.variables.list()) == 0 diff --git a/tools/functional/cli/conftest.py b/tools/functional/cli/conftest.py deleted file mode 100644 index ba94dcb..0000000 --- a/tools/functional/cli/conftest.py +++ /dev/null @@ -1,21 +0,0 @@ -import pytest - - -@pytest.fixture -def gitlab_cli(script_runner, gitlab_config): - """Wrapper fixture to help make test cases less verbose.""" - - def _gitlab_cli(subcommands): - """ - Return a script_runner.run method that takes a default gitlab - command, and subcommands passed as arguments inside test cases. - """ - command = ["gitlab", "--config-file", gitlab_config] - - for subcommand in subcommands: - # ensure we get strings (e.g from IDs) - command.append(str(subcommand)) - - return script_runner.run(*command) - - return _gitlab_cli diff --git a/tools/functional/cli/test_cli_artifacts.py b/tools/functional/cli/test_cli_artifacts.py deleted file mode 100644 index 4cb69aa..0000000 --- a/tools/functional/cli/test_cli_artifacts.py +++ /dev/null @@ -1,51 +0,0 @@ -import subprocess -import sys -import textwrap -import time -from io import BytesIO -from zipfile import is_zipfile - -import pytest - -content = textwrap.dedent( - """\ - test-artifact: - script: echo "test" > artifact.txt - artifacts: - untracked: true - """ -) -data = { - "file_path": ".gitlab-ci.yml", - "branch": "master", - "content": content, - "commit_message": "Initial commit", -} - - -@pytest.mark.skipif(sys.version_info < (3, 8), reason="I am the walrus") -def test_cli_artifacts(capsysbinary, gitlab_config, gitlab_runner, project): - project.files.create(data) - - while not (jobs := project.jobs.list(scope="success")): - time.sleep(0.5) - - job = project.jobs.get(jobs[0].id) - cmd = [ - "gitlab", - "--config-file", - gitlab_config, - "project-job", - "artifacts", - "--id", - str(job.id), - "--project-id", - str(project.id), - ] - - with capsysbinary.disabled(): - artifacts = subprocess.check_output(cmd) - assert isinstance(artifacts, bytes) - - artifacts_zip = BytesIO(artifacts) - assert is_zipfile(artifacts_zip) diff --git a/tools/functional/cli/test_cli_packages.py b/tools/functional/cli/test_cli_packages.py deleted file mode 100644 index a3734a2..0000000 --- a/tools/functional/cli/test_cli_packages.py +++ /dev/null @@ -1,12 +0,0 @@ -def test_list_project_packages(gitlab_cli, project): - cmd = ["project-package", "list", "--project-id", project.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_group_packages(gitlab_cli, group): - cmd = ["group-package", "list", "--group-id", group.id] - ret = gitlab_cli(cmd) - - assert ret.success diff --git a/tools/functional/cli/test_cli_v4.py b/tools/functional/cli/test_cli_v4.py deleted file mode 100644 index a63c1b1..0000000 --- a/tools/functional/cli/test_cli_v4.py +++ /dev/null @@ -1,715 +0,0 @@ -import os -import time - - -def test_create_project(gitlab_cli): - name = "test-project1" - - cmd = ["project", "create", "--name", name] - ret = gitlab_cli(cmd) - - assert ret.success - assert name in ret.stdout - - -def test_update_project(gitlab_cli, project): - description = "My New Description" - - cmd = ["project", "update", "--id", project.id, "--description", description] - ret = gitlab_cli(cmd) - - assert ret.success - assert description in ret.stdout - - -def test_create_group(gitlab_cli): - name = "test-group1" - path = "group1" - - cmd = ["group", "create", "--name", name, "--path", path] - ret = gitlab_cli(cmd) - - assert ret.success - assert name in ret.stdout - assert path in ret.stdout - - -def test_update_group(gitlab_cli, gl, group): - description = "My New Description" - - cmd = ["group", "update", "--id", group.id, "--description", description] - ret = gitlab_cli(cmd) - - assert ret.success - - group = gl.groups.get(group.id) - assert group.description == description - - -def test_create_user(gitlab_cli, gl): - email = "fake@email.com" - username = "user1" - name = "User One" - password = "fakepassword" - - cmd = [ - "user", - "create", - "--email", - email, - "--username", - username, - "--name", - name, - "--password", - password, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - user = gl.users.list(username=username)[0] - - assert user.email == email - assert user.username == username - assert user.name == name - - -def test_get_user_by_id(gitlab_cli, user): - cmd = ["user", "get", "--id", user.id] - ret = gitlab_cli(cmd) - - assert ret.success - assert str(user.id) in ret.stdout - - -def test_list_users_verbose_output(gitlab_cli): - cmd = ["-v", "user", "list"] - ret = gitlab_cli(cmd) - - assert ret.success - assert "avatar-url" in ret.stdout - - -def test_cli_args_not_in_output(gitlab_cli): - cmd = ["-v", "user", "list"] - ret = gitlab_cli(cmd) - - assert "config-file" not in ret.stdout - - -def test_add_member_to_project(gitlab_cli, project, user): - access_level = "40" - - cmd = [ - "project-member", - "create", - "--project-id", - project.id, - "--user-id", - user.id, - "--access-level", - access_level, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_user_memberships(gitlab_cli, user): - cmd = ["user-membership", "list", "--user-id", user.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_project_create_file(gitlab_cli, project): - file_path = "README" - branch = "master" - content = "CONTENT" - commit_message = "Initial commit" - - cmd = [ - "project-file", - "create", - "--project-id", - project.id, - "--file-path", - file_path, - "--branch", - branch, - "--content", - content, - "--commit-message", - commit_message, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_project_issue(gitlab_cli, project): - title = "my issue" - description = "my issue description" - - cmd = [ - "project-issue", - "create", - "--project-id", - project.id, - "--title", - title, - "--description", - description, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert title in ret.stdout - - -def test_create_issue_note(gitlab_cli, issue): - body = "body" - - cmd = [ - "project-issue-note", - "create", - "--project-id", - issue.project_id, - "--issue-iid", - issue.iid, - "--body", - body, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_branch(gitlab_cli, project): - branch = "branch1" - - cmd = [ - "project-branch", - "create", - "--project-id", - project.id, - "--branch", - branch, - "--ref", - "master", - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_merge_request(gitlab_cli, project): - branch = "branch1" - - cmd = [ - "project-merge-request", - "create", - "--project-id", - project.id, - "--source-branch", - branch, - "--target-branch", - "master", - "--title", - "Update README", - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -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 = { - "branch": mr.source_branch, - "file_path": "README2", - "content": "Content", - "commit_message": "Pre-merge commit", - } - project.files.create(file_data) - time.sleep(2) - - cmd = [ - "project-merge-request", - "merge", - "--project-id", - project.id, - "--iid", - mr.iid, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_revert_commit(gitlab_cli, project): - commit = project.commits.list()[0] - - cmd = [ - "project-commit", - "revert", - "--project-id", - project.id, - "--id", - commit.id, - "--branch", - "master", - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_get_commit_signature_not_found(gitlab_cli, project): - commit = project.commits.list()[0] - - cmd = ["project-commit", "signature", "--project-id", project.id, "--id", commit.id] - ret = gitlab_cli(cmd) - - assert not ret.success - assert "404 Signature Not Found" in ret.stderr - - -def test_create_project_label(gitlab_cli, project): - name = "prjlabel1" - description = "prjlabel1 description" - color = "#112233" - - cmd = [ - "-v", - "project-label", - "create", - "--project-id", - project.id, - "--name", - name, - "--description", - description, - "--color", - color, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_project_labels(gitlab_cli, project): - cmd = ["-v", "project-label", "list", "--project-id", project.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_update_project_label(gitlab_cli, label): - new_label = "prjlabel2" - new_description = "prjlabel2 description" - new_color = "#332211" - - cmd = [ - "-v", - "project-label", - "update", - "--project-id", - label.project_id, - "--name", - label.name, - "--new-name", - new_label, - "--description", - new_description, - "--color", - new_color, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_delete_project_label(gitlab_cli, label): - # TODO: due to update above, we'd need a function-scope label fixture - label_name = "prjlabel2" - - cmd = [ - "-v", - "project-label", - "delete", - "--project-id", - label.project_id, - "--name", - label_name, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_group_label(gitlab_cli, group): - name = "grouplabel1" - description = "grouplabel1 description" - color = "#112233" - - cmd = [ - "-v", - "group-label", - "create", - "--group-id", - group.id, - "--name", - name, - "--description", - description, - "--color", - color, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_group_labels(gitlab_cli, group): - cmd = ["-v", "group-label", "list", "--group-id", group.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_update_group_label(gitlab_cli, group_label): - new_label = "grouplabel2" - new_description = "grouplabel2 description" - new_color = "#332211" - - cmd = [ - "-v", - "group-label", - "update", - "--group-id", - group_label.group_id, - "--name", - group_label.name, - "--new-name", - new_label, - "--description", - new_description, - "--color", - new_color, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_delete_group_label(gitlab_cli, group_label): - # TODO: due to update above, we'd need a function-scope label fixture - new_label = "grouplabel2" - - cmd = [ - "-v", - "group-label", - "delete", - "--group-id", - group_label.group_id, - "--name", - new_label, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_project_variable(gitlab_cli, project): - key = "junk" - value = "car" - - cmd = [ - "-v", - "project-variable", - "create", - "--project-id", - project.id, - "--key", - key, - "--value", - value, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_get_project_variable(gitlab_cli, variable): - cmd = [ - "-v", - "project-variable", - "get", - "--project-id", - variable.project_id, - "--key", - variable.key, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_update_project_variable(gitlab_cli, variable): - new_value = "bus" - - cmd = [ - "-v", - "project-variable", - "update", - "--project-id", - variable.project_id, - "--key", - variable.key, - "--value", - new_value, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_project_variables(gitlab_cli, project): - cmd = ["-v", "project-variable", "list", "--project-id", project.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_delete_project_variable(gitlab_cli, variable): - cmd = [ - "-v", - "project-variable", - "delete", - "--project-id", - variable.project_id, - "--key", - variable.key, - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -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) - - assert ret.success - - -def test_project_upload_file(gitlab_cli, project): - cmd = [ - "project", - "upload", - "--id", - project.id, - "--filename", - __file__, - "--filepath", - os.path.realpath(__file__), - ] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_get_application_settings(gitlab_cli): - cmd = ["application-settings", "get"] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_update_application_settings(gitlab_cli): - cmd = ["application-settings", "update", "--signup-enabled", "false"] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_create_project_with_values_from_file(gitlab_cli, tmpdir): - name = "gitlab-project-from-file" - description = "Multiline\n\nData\n" - from_file = tmpdir.join(name) - from_file.write(description) - from_file_path = f"@{str(from_file)}" - - cmd = [ - "-v", - "project", - "create", - "--name", - name, - "--description", - from_file_path, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert description in ret.stdout - - -def test_create_project_deploy_token(gitlab_cli, project): - name = "project-token" - username = "root" - expires_at = "2021-09-09" - scopes = "read_registry" - - cmd = [ - "-v", - "project-deploy-token", - "create", - "--project-id", - project.id, - "--name", - name, - "--username", - username, - "--expires-at", - expires_at, - "--scopes", - scopes, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert name in ret.stdout - assert username in ret.stdout - assert expires_at in ret.stdout - assert scopes in ret.stdout - - -def test_list_all_deploy_tokens(gitlab_cli, deploy_token): - cmd = ["-v", "deploy-token", "list"] - ret = gitlab_cli(cmd) - - assert ret.success - assert deploy_token.name in ret.stdout - assert str(deploy_token.id) in ret.stdout - assert deploy_token.username in ret.stdout - assert deploy_token.expires_at in ret.stdout - assert deploy_token.scopes[0] in ret.stdout - - -def test_list_project_deploy_tokens(gitlab_cli, deploy_token): - cmd = [ - "-v", - "project-deploy-token", - "list", - "--project-id", - deploy_token.project_id, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert deploy_token.name in ret.stdout - assert str(deploy_token.id) in ret.stdout - assert deploy_token.username in ret.stdout - assert deploy_token.expires_at in ret.stdout - assert deploy_token.scopes[0] in ret.stdout - - -def test_delete_project_deploy_token(gitlab_cli, deploy_token): - cmd = [ - "-v", - "project-deploy-token", - "delete", - "--project-id", - deploy_token.project_id, - "--id", - deploy_token.id, - ] - ret = gitlab_cli(cmd) - - assert ret.success - # TODO assert not in list - - -def test_create_group_deploy_token(gitlab_cli, group): - name = "group-token" - username = "root" - expires_at = "2021-09-09" - scopes = "read_registry" - - cmd = [ - "-v", - "group-deploy-token", - "create", - "--group-id", - group.id, - "--name", - name, - "--username", - username, - "--expires-at", - expires_at, - "--scopes", - scopes, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert name in ret.stdout - assert username in ret.stdout - assert expires_at in ret.stdout - assert scopes in ret.stdout - - -def test_list_group_deploy_tokens(gitlab_cli, group_deploy_token): - cmd = [ - "-v", - "group-deploy-token", - "list", - "--group-id", - group_deploy_token.group_id, - ] - ret = gitlab_cli(cmd) - - assert ret.success - assert group_deploy_token.name in ret.stdout - assert str(group_deploy_token.id) in ret.stdout - assert group_deploy_token.username in ret.stdout - assert group_deploy_token.expires_at in ret.stdout - assert group_deploy_token.scopes[0] in ret.stdout - - -def test_delete_group_deploy_token(gitlab_cli, group_deploy_token): - cmd = [ - "-v", - "group-deploy-token", - "delete", - "--group-id", - group_deploy_token.group_id, - "--id", - group_deploy_token.id, - ] - ret = gitlab_cli(cmd) - - assert ret.success - # TODO assert not in list - - -def test_delete_project(gitlab_cli, project): - cmd = ["project", "delete", "--id", project.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_delete_group(gitlab_cli, group): - cmd = ["group", "delete", "--id", group.id] - ret = gitlab_cli(cmd) - - assert ret.success diff --git a/tools/functional/cli/test_cli_variables.py b/tools/functional/cli/test_cli_variables.py deleted file mode 100644 index 9b1b16d..0000000 --- a/tools/functional/cli/test_cli_variables.py +++ /dev/null @@ -1,19 +0,0 @@ -def test_list_instance_variables(gitlab_cli, gl): - cmd = ["variable", "list"] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_group_variables(gitlab_cli, group): - cmd = ["group-variable", "list", "--group-id", group.id] - ret = gitlab_cli(cmd) - - assert ret.success - - -def test_list_project_variables(gitlab_cli, project): - cmd = ["project-variable", "list", "--project-id", project.id] - ret = gitlab_cli(cmd) - - assert ret.success diff --git a/tools/functional/conftest.py b/tools/functional/conftest.py deleted file mode 100644 index 634713d..0000000 --- a/tools/functional/conftest.py +++ /dev/null @@ -1,462 +0,0 @@ -import tempfile -import time -import uuid -from pathlib import Path -from subprocess import check_output - -import pytest - -import gitlab - - -def reset_gitlab(gl): - # previously tools/reset_gitlab.py - for project in gl.projects.list(): - project.delete() - for group in gl.groups.list(): - group.delete() - for variable in gl.variables.list(): - variable.delete() - for user in gl.users.list(): - if user.username != "root": - user.delete() - - -def set_token(container, rootdir): - set_token_rb = rootdir / "fixtures" / "set_token.rb" - - with open(set_token_rb, "r") as f: - set_token_command = f.read().strip() - - rails_command = [ - "docker", - "exec", - container, - "gitlab-rails", - "runner", - set_token_command, - ] - output = check_output(rails_command).decode().strip() - - return output - - -def pytest_report_collectionfinish(config, startdir, items): - return [ - "", - "Starting GitLab container.", - "Waiting for GitLab to reconfigure.", - "This may take a few minutes.", - ] - - -@pytest.fixture(scope="session") -def temp_dir(): - return Path(tempfile.gettempdir()) - - -@pytest.fixture(scope="session") -def test_dir(pytestconfig): - return pytestconfig.rootdir / "tools" / "functional" - - -@pytest.fixture(scope="session") -def docker_compose_file(test_dir): - return test_dir / "fixtures" / "docker-compose.yml" - - -@pytest.fixture(scope="session") -def check_is_alive(): - """ - Return a healthcheck function fixture for the GitLab container spinup. - """ - - def _check(container): - logs = ["docker", "logs", container] - return "gitlab Reconfigured!" in check_output(logs).decode() - - return _check - - -@pytest.fixture -def wait_for_sidekiq(gl): - """ - Return a helper function to wait until there are no busy sidekiq processes. - - Use this with asserts for slow tasks (group/project/user creation/deletion). - """ - - def _wait(timeout=30, step=0.5): - for _ in range(timeout): - time.sleep(step) - busy = False - processes = gl.sidekiq.process_metrics()["processes"] - for process in processes: - if process["busy"]: - busy = True - if not busy: - return True - return False - - return _wait - - -@pytest.fixture(scope="session") -def gitlab_config(check_is_alive, docker_ip, docker_services, temp_dir, test_dir): - config_file = temp_dir / "python-gitlab.cfg" - port = docker_services.port_for("gitlab", 80) - - docker_services.wait_until_responsive( - timeout=200, pause=5, check=lambda: check_is_alive("gitlab-test") - ) - - token = set_token("gitlab-test", rootdir=test_dir) - - config = f"""[global] -default = local -timeout = 60 - -[local] -url = http://{docker_ip}:{port} -private_token = {token} -api_version = 4""" - - with open(config_file, "w") as f: - f.write(config) - - return config_file - - -@pytest.fixture(scope="session") -def gl(gitlab_config): - """Helper instance to make fixtures and asserts directly via the API.""" - - instance = gitlab.Gitlab.from_config("local", [gitlab_config]) - reset_gitlab(instance) - - return instance - - -@pytest.fixture(scope="session") -def gitlab_runner(gl): - container = "gitlab-runner-test" - runner_name = "python-gitlab-runner" - token = "registration-token" - url = "http://gitlab" - - docker_exec = ["docker", "exec", container, "gitlab-runner"] - register = [ - "register", - "--run-untagged", - "--non-interactive", - "--registration-token", - token, - "--name", - runner_name, - "--url", - url, - "--clone-url", - url, - "--executor", - "shell", - ] - unregister = ["unregister", "--name", runner_name] - - yield check_output(docker_exec + register).decode() - - check_output(docker_exec + unregister).decode() - - -@pytest.fixture(scope="module") -def group(gl): - """Group fixture for group API resource tests.""" - _id = uuid.uuid4().hex - data = { - "name": f"test-group-{_id}", - "path": f"group-{_id}", - } - group = gl.groups.create(data) - - yield group - - try: - group.delete() - except gitlab.exceptions.GitlabDeleteError as e: - print(f"Group already deleted: {e}") - - -@pytest.fixture(scope="module") -def project(gl): - """Project fixture for project API resource tests.""" - _id = uuid.uuid4().hex - name = f"test-project-{_id}" - - project = gl.projects.create(name=name) - - yield project - - try: - project.delete() - except gitlab.exceptions.GitlabDeleteError as e: - print(f"Project already deleted: {e}") - - -@pytest.fixture(scope="function") -def merge_request(project, wait_for_sidekiq): - """Fixture used to create a merge_request. - - It will create a branch, add a commit to the branch, and then create a - merge request against project.default_branch. The MR will be returned. - - When finished any created merge requests and branches will be deleted. - - NOTE: No attempt is made to restore project.default_branch to its previous - state. So if the merge request is merged then its content will be in the - project.default_branch branch. - """ - - to_delete = [] - - def _merge_request(*, source_branch: str): - # Wait for processes to be done before we start... - # 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" - - project.refresh() # Gets us the current default branch - project.branches.create( - {"branch": source_branch, "ref": project.default_branch} - ) - # NOTE(jlvillal): Must create a commit in the new branch before we can - # create an MR that will work. - project.files.create( - { - "file_path": f"README.{source_branch}", - "branch": source_branch, - "content": "Initial content", - "commit_message": "New commit in new branch", - } - ) - mr = project.mergerequests.create( - { - "source_branch": source_branch, - "target_branch": project.default_branch, - "title": "Should remove source branch", - "remove_source_branch": True, - } - ) - result = wait_for_sidekiq(timeout=60) - assert result is True, "sidekiq process should have terminated but did not" - - mr_iid = mr.iid - for _ in range(60): - mr = project.mergerequests.get(mr_iid) - if mr.merge_status != "checking": - break - time.sleep(0.5) - assert mr.merge_status != "checking" - - to_delete.append((mr.iid, source_branch)) - return mr - - yield _merge_request - - for mr_iid, source_branch in to_delete: - project.mergerequests.delete(mr_iid) - try: - project.branches.delete(source_branch) - except gitlab.exceptions.GitlabDeleteError: - # Ignore if branch was already deleted - pass - - -@pytest.fixture(scope="module") -def project_file(project): - """File fixture for tests requiring a project with files and branches.""" - project_file = project.files.create( - { - "file_path": "README", - "branch": "master", - "content": "Initial content", - "commit_message": "Initial commit", - } - ) - - return project_file - - -@pytest.fixture(scope="function") -def release(project, project_file): - _id = uuid.uuid4().hex - name = f"test-release-{_id}" - - project.refresh() # Gets us the current default branch - release = project.releases.create( - { - "name": name, - "tag_name": _id, - "description": "description", - "ref": project.default_branch, - } - ) - - return release - - -@pytest.fixture(scope="module") -def user(gl): - """User fixture for user API resource tests.""" - _id = uuid.uuid4().hex - email = f"user{_id}@email.com" - username = f"user{_id}" - name = f"User {_id}" - password = "fakepassword" - - user = gl.users.create(email=email, username=username, name=name, password=password) - - yield user - - try: - user.delete() - except gitlab.exceptions.GitlabDeleteError as e: - print(f"User already deleted: {e}") - - -@pytest.fixture(scope="module") -def issue(project): - """Issue fixture for issue API resource tests.""" - _id = uuid.uuid4().hex - data = {"title": f"Issue {_id}", "description": f"Issue {_id} description"} - - return project.issues.create(data) - - -@pytest.fixture(scope="module") -def milestone(project): - _id = uuid.uuid4().hex - data = {"title": f"milestone{_id}"} - - return project.milestones.create(data) - - -@pytest.fixture(scope="module") -def label(project): - """Label fixture for project label API resource tests.""" - _id = uuid.uuid4().hex - data = { - "name": f"prjlabel{_id}", - "description": f"prjlabel1 {_id} description", - "color": "#112233", - } - - return project.labels.create(data) - - -@pytest.fixture(scope="module") -def group_label(group): - """Label fixture for group label API resource tests.""" - _id = uuid.uuid4().hex - data = { - "name": f"grplabel{_id}", - "description": f"grplabel1 {_id} description", - "color": "#112233", - } - - return group.labels.create(data) - - -@pytest.fixture(scope="module") -def variable(project): - """Variable fixture for project variable API resource tests.""" - _id = uuid.uuid4().hex - data = {"key": f"var{_id}", "value": f"Variable {_id}"} - - return project.variables.create(data) - - -@pytest.fixture(scope="module") -def deploy_token(project): - """Deploy token fixture for project deploy token API resource tests.""" - _id = uuid.uuid4().hex - data = { - "name": f"token-{_id}", - "username": "root", - "expires_at": "2021-09-09", - "scopes": "read_registry", - } - - return project.deploytokens.create(data) - - -@pytest.fixture(scope="module") -def group_deploy_token(group): - """Deploy token fixture for group deploy token API resource tests.""" - _id = uuid.uuid4().hex - data = { - "name": f"group-token-{_id}", - "username": "root", - "expires_at": "2021-09-09", - "scopes": "read_registry", - } - - return group.deploytokens.create(data) - - -@pytest.fixture(scope="session") -def GPG_KEY(): - return """-----BEGIN PGP PUBLIC KEY BLOCK----- - -mQENBFn5mzYBCADH6SDVPAp1zh/hxmTi0QplkOfExBACpuY6OhzNdIg+8/528b3g -Y5YFR6T/HLv/PmeHskUj21end1C0PNG2T9dTx+2Vlh9ISsSG1kyF9T5fvMR3bE0x -Dl6S489CXZrjPTS9SHk1kF+7dwjUxLJyxF9hPiSihFefDFu3NeOtG/u8vbC1mewQ -ZyAYue+mqtqcCIFFoBz7wHKMWjIVSJSyTkXExu4OzpVvy3l2EikbvavI3qNz84b+ -Mgkv/kiBlNoCy3CVuPk99RYKZ3lX1vVtqQ0OgNGQvb4DjcpyjmbKyibuZwhDjIOh -au6d1OyEbayTntd+dQ4j9EMSnEvm/0MJ4eXPABEBAAG0G0dpdGxhYlRlc3QxIDxm -YWtlQGZha2UudGxkPokBNwQTAQgAIQUCWfmbNgIbAwULCQgHAgYVCAkKCwIEFgID -AQIeAQIXgAAKCRBgxELHf8f3hF3yB/wNJlWPKY65UsB4Lo0hs1OxdxCDqXogSi0u -6crDEIiyOte62pNZKzWy8TJcGZvznRTZ7t8hXgKFLz3PRMcl+vAiRC6quIDUj+2V -eYfwaItd1lUfzvdCaC7Venf4TQ74f5vvNg/zoGwE6eRoSbjlLv9nqsxeA0rUBUQL -LYikWhVMP3TrlfgfduYvh6mfgh57BDLJ9kJVpyfxxx9YLKZbaas9sPa6LgBtR555 -JziUxHmbEv8XCsUU8uoFeP1pImbNBplqE3wzJwzOMSmmch7iZzrAwfN7N2j3Wj0H -B5kQddJ9dmB4BbU0IXGhWczvdpxboI2wdY8a1JypxOdePoph/43iuQENBFn5mzYB -CADnTPY0Zf3d9zLjBNgIb3yDl94uOcKCq0twNmyjMhHzGqw+UMe9BScy34GL94Al -xFRQoaL+7P8hGsnsNku29A/VDZivcI+uxTx4WQ7OLcn7V0bnHV4d76iky2ufbUt/ -GofthjDs1SonePO2N09sS4V4uK0d5N4BfCzzXgvg8etCLxNmC9BGt7AaKUUzKBO4 -2QvNNaC2C/8XEnOgNWYvR36ylAXAmo0sGFXUsBCTiq1fugS9pwtaS2JmaVpZZ3YT -pMZlS0+SjC5BZYFqSmKCsA58oBRzCxQz57nR4h5VEflgD+Hy0HdW0UHETwz83E6/ -U0LL6YyvhwFr6KPq5GxinSvfABEBAAGJAR8EGAEIAAkFAln5mzYCGwwACgkQYMRC -x3/H94SJgwgAlKQb10/xcL/epdDkR7vbiei7huGLBpRDb/L5fM8B5W77Qi8Xmuqj -cCu1j99ZCA5hs/vwVn8j8iLSBGMC5gxcuaar/wtmiaEvT9fO/h6q4opG7NcuiJ8H -wRj8ccJmRssNqDD913PLz7T40Ts62blhrEAlJozGVG/q7T3RAZcskOUHKeHfc2RI -YzGsC/I9d7k6uxAv1L9Nm5F2HaAQDzhkdd16nKkGaPGR35cT1JLInkfl5cdm7ldN -nxs4TLO3kZjUTgWKdhpgRNF5hwaz51ZjpebaRf/ZqRuNyX4lIRolDxzOn/+O1o8L -qG2ZdhHHmSK2LaQLFiSprUkikStNU9BqSQ== -=5OGa ------END PGP PUBLIC KEY BLOCK-----""" - - -@pytest.fixture(scope="session") -def SSH_KEY(): - return ( - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDZAjAX8vTiHD7Yi3/EzuVaDChtih" - "79HyJZ6H9dEqxFfmGA1YnncE0xujQ64TCebhkYJKzmTJCImSVkOu9C4hZgsw6eE76n" - "+Cg3VwEeDUFy+GXlEJWlHaEyc3HWioxgOALbUp3rOezNh+d8BDwwqvENGoePEBsz5l" - "a6WP5lTi/HJIjAl6Hu+zHgdj1XVExeH+S52EwpZf/ylTJub0Bl5gHwf/siVE48mLMI" - "sqrukXTZ6Zg+8EHAIvIQwJ1dKcXe8P5IoLT7VKrbkgAnolS0I8J+uH7KtErZJb5oZh" - "S4OEwsNpaXMAr+6/wWSpircV2/e7sFLlhlKBC4Iq1MpqlZ7G3p foo@bar" - ) - - -@pytest.fixture(scope="session") -def DEPLOY_KEY(): - return ( - "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDFdRyjJQh+1niBpXqE2I8dzjG" - "MXFHlRjX9yk/UfOn075IdaockdU58sw2Ai1XIWFpZpfJkW7z+P47ZNSqm1gzeXI" - "rtKa9ZUp8A7SZe8vH4XVn7kh7bwWCUirqtn8El9XdqfkzOs/+FuViriUWoJVpA6" - "WZsDNaqINFKIA5fj/q8XQw+BcS92L09QJg9oVUuH0VVwNYbU2M2IRmSpybgC/gu" - "uWTrnCDMmLItksATifLvRZwgdI8dr+q6tbxbZknNcgEPrI2jT0hYN9ZcjNeWuyv" - "rke9IepE7SPBT41C+YtUX4dfDZDmczM1cE0YL/krdUCfuZHMa4ZS2YyNd6slufc" - "vn bar@foo" - ) diff --git a/tools/functional/ee-test.py b/tools/functional/ee-test.py deleted file mode 100755 index 3a99951..0000000 --- a/tools/functional/ee-test.py +++ /dev/null @@ -1,158 +0,0 @@ -#!/usr/bin/env python - -import gitlab - -P1 = "root/project1" -P2 = "root/project2" -MR_P1 = 1 -I_P1 = 1 -I_P2 = 1 -EPIC_ISSUES = [4, 5] -G1 = "group1" -LDAP_CN = "app1" -LDAP_PROVIDER = "ldapmain" - - -def start_log(message): - print("Testing %s... " % message, end="") - - -def end_log(): - print("OK") - - -gl = gitlab.Gitlab.from_config("ee") -project1 = gl.projects.get(P1) -project2 = gl.projects.get(P2) -issue_p1 = project1.issues.get(I_P1) -issue_p2 = project2.issues.get(I_P2) -group1 = gl.groups.get(G1) -mr = project1.mergerequests.get(1) - -start_log("MR approvals") -approval = project1.approvals.get() -v = approval.reset_approvals_on_push -approval.reset_approvals_on_push = not v -approval.save() -approval = project1.approvals.get() -assert v != approval.reset_approvals_on_push -project1.approvals.set_approvers(1, [1], []) -approval = project1.approvals.get() -assert approval.approvers[0]["user"]["id"] == 1 - -approval = mr.approvals.get() -approval.approvals_required = 2 -approval.save() -approval = mr.approvals.get() -assert approval.approvals_required == 2 -approval.approvals_required = 3 -approval.save() -approval = mr.approvals.get() -assert approval.approvals_required == 3 -mr.approvals.set_approvers(1, [1], []) -approval = mr.approvals.get() -assert approval.approvers[0]["user"]["id"] == 1 - -ars = project1.approvalrules.list(all=True) -assert len(ars) == 0 -project1.approvalrules.create( - {"name": "approval-rule", "approvals_required": 1, "group_ids": [group1.id]} -) -ars = project1.approvalrules.list(all=True) -assert len(ars) == 1 -assert ars[0].approvals_required == 2 -ars[0].save() -ars = project1.approvalrules.list(all=True) -assert len(ars) == 1 -assert ars[0].approvals_required == 2 -ars[0].delete() -ars = project1.approvalrules.list(all=True) -assert len(ars) == 0 -end_log() - -start_log("geo nodes") -# very basic tests because we only have 1 node... -nodes = gl.geonodes.list() -status = gl.geonodes.status() -end_log() - -start_log("issue links") -# bit of cleanup just in case -for link in issue_p1.links.list(): - issue_p1.links.delete(link.issue_link_id) - -src, dst = issue_p1.links.create({"target_project_id": P2, "target_issue_iid": I_P2}) -links = issue_p1.links.list() -link_id = links[0].issue_link_id -issue_p1.links.delete(link_id) -end_log() - -start_log("LDAP links") -# bit of cleanup just in case -if hasattr(group1, "ldap_group_links"): - for link in group1.ldap_group_links: - group1.delete_ldap_group_link(link["cn"], link["provider"]) -assert gl.ldapgroups.list() -group1.add_ldap_group_link(LDAP_CN, 30, LDAP_PROVIDER) -group1.ldap_sync() -group1.delete_ldap_group_link(LDAP_CN) -end_log() - -start_log("boards") -# bit of cleanup just in case -for board in project1.boards.list(): - if board.name == "testboard": - board.delete() -board = project1.boards.create({"name": "testboard"}) -board = project1.boards.get(board.id) -project1.boards.delete(board.id) - -for board in group1.boards.list(): - if board.name == "testboard": - board.delete() -board = group1.boards.create({"name": "testboard"}) -board = group1.boards.get(board.id) -group1.boards.delete(board.id) -end_log() - -start_log("push rules") -pr = project1.pushrules.get() -if pr: - pr.delete() -pr = project1.pushrules.create({"deny_delete_tag": True}) -pr.deny_delete_tag = False -pr.save() -pr = project1.pushrules.get() -assert pr is not None -assert pr.deny_delete_tag is False -pr.delete() -end_log() - -start_log("license") -license = gl.get_license() -assert "user_limit" in license -try: - gl.set_license("dummykey") -except Exception as e: - assert "The license key is invalid." in e.error_message -end_log() - -start_log("epics") -epic = group1.epics.create({"title": "Test epic"}) -epic.title = "Fixed title" -epic.labels = ["label1", "label2"] -epic.save() -epic = group1.epics.get(epic.iid) -assert epic.title == "Fixed title" -assert len(group1.epics.list()) - -# issues -assert not epic.issues.list() -for i in EPIC_ISSUES: - epic.issues.create({"issue_id": i}) -assert len(EPIC_ISSUES) == len(epic.issues.list()) -for ei in epic.issues.list(): - ei.delete() - -epic.delete() -end_log() diff --git a/tools/functional/fixtures/.env b/tools/functional/fixtures/.env deleted file mode 100644 index eacfb28..0000000 --- a/tools/functional/fixtures/.env +++ /dev/null @@ -1,2 +0,0 @@ -GITLAB_IMAGE=gitlab/gitlab-ce -GITLAB_TAG=13.11.4-ce.0 diff --git a/tools/functional/fixtures/avatar.png b/tools/functional/fixtures/avatar.png deleted file mode 100644 index a3a767c..0000000 Binary files a/tools/functional/fixtures/avatar.png and /dev/null differ diff --git a/tools/functional/fixtures/docker-compose.yml b/tools/functional/fixtures/docker-compose.yml deleted file mode 100644 index a0794d6..0000000 --- a/tools/functional/fixtures/docker-compose.yml +++ /dev/null @@ -1,46 +0,0 @@ -version: '3' - -networks: - gitlab-network: - name: gitlab-network - -services: - gitlab: - image: '${GITLAB_IMAGE}:${GITLAB_TAG}' - container_name: 'gitlab-test' - hostname: 'gitlab.test' - privileged: true # Just in case https://gitlab.com/gitlab-org/omnibus-gitlab/-/issues/1350 - environment: - GITLAB_OMNIBUS_CONFIG: | - external_url 'http://gitlab.test' - gitlab_rails['initial_root_password'] = '5iveL!fe' - gitlab_rails['initial_shared_runners_registration_token'] = 'registration-token' - registry['enable'] = false - nginx['redirect_http_to_https'] = false - nginx['listen_port'] = 80 - nginx['listen_https'] = false - pages_external_url 'http://pages.gitlab.lxd' - gitlab_pages['enable'] = true - gitlab_pages['inplace_chroot'] = true - prometheus['enable'] = false - alertmanager['enable'] = false - node_exporter['enable'] = false - redis_exporter['enable'] = false - postgres_exporter['enable'] = false - pgbouncer_exporter['enable'] = false - gitlab_exporter['enable'] = false - grafana['enable'] = false - letsencrypt['enable'] = false - ports: - - '8080:80' - - '2222:22' - networks: - - gitlab-network - - gitlab-runner: - image: gitlab/gitlab-runner:latest - container_name: 'gitlab-runner-test' - depends_on: - - gitlab - networks: - - gitlab-network diff --git a/tools/functional/fixtures/set_token.rb b/tools/functional/fixtures/set_token.rb deleted file mode 100644 index 735dcd5..0000000 --- a/tools/functional/fixtures/set_token.rb +++ /dev/null @@ -1,9 +0,0 @@ -# https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#programmatically-creating-a-personal-access-token - -user = User.find_by_username('root') - -token = user.personal_access_tokens.create(scopes: [:api, :sudo], name: 'default'); -token.set_token('python-gitlab-token'); -token.save! - -puts token.token diff --git a/tox.ini b/tox.ini index d3dfdfc..a1b1b42 100644 --- a/tox.ini +++ b/tox.ini @@ -97,9 +97,9 @@ script_launch_mode = subprocess [testenv:cli_func_v4] deps = -r{toxinidir}/docker-requirements.txt commands = - pytest --cov --cov-report xml tools/functional/cli {posargs} + pytest --cov --cov-report xml tests/functional/cli {posargs} [testenv:py_func_v4] deps = -r{toxinidir}/docker-requirements.txt commands = - pytest --cov --cov-report xml tools/functional/api {posargs} + pytest --cov --cov-report xml tests/functional/api {posargs} -- cgit v1.2.1