diff options
Diffstat (limited to 'tools/functional/api')
| -rw-r--r-- | tools/functional/api/test_clusters.py | 46 | ||||
| -rw-r--r-- | tools/functional/api/test_current_user.py | 42 | ||||
| -rw-r--r-- | tools/functional/api/test_deploy_keys.py | 12 | ||||
| -rw-r--r-- | tools/functional/api/test_deploy_tokens.py | 36 | ||||
| -rw-r--r-- | tools/functional/api/test_gitlab.py | 187 | ||||
| -rw-r--r-- | tools/functional/api/test_groups.py | 190 | ||||
| -rw-r--r-- | tools/functional/api/test_import_export.py | 61 | ||||
| -rw-r--r-- | tools/functional/api/test_issues.py | 89 | ||||
| -rw-r--r-- | tools/functional/api/test_merge_requests.py | 96 | ||||
| -rw-r--r-- | tools/functional/api/test_projects.py | 298 | ||||
| -rw-r--r-- | tools/functional/api/test_repository.py | 126 | ||||
| -rw-r--r-- | tools/functional/api/test_snippets.py | 74 | ||||
| -rw-r--r-- | tools/functional/api/test_users.py | 154 |
13 files changed, 1403 insertions, 8 deletions
diff --git a/tools/functional/api/test_clusters.py b/tools/functional/api/test_clusters.py new file mode 100644 index 0000000..8930aad --- /dev/null +++ b/tools/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/tools/functional/api/test_current_user.py b/tools/functional/api/test_current_user.py new file mode 100644 index 0000000..5802457 --- /dev/null +++ b/tools/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/tools/functional/api/test_deploy_keys.py b/tools/functional/api/test_deploy_keys.py new file mode 100644 index 0000000..18828a2 --- /dev/null +++ b/tools/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/tools/functional/api/test_deploy_tokens.py b/tools/functional/api/test_deploy_tokens.py new file mode 100644 index 0000000..efcf8b1 --- /dev/null +++ b/tools/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/tools/functional/api/test_gitlab.py b/tools/functional/api/test_gitlab.py index 5cf3418..347213c 100644 --- a/tools/functional/api/test_gitlab.py +++ b/tools/functional/api/test_gitlab.py @@ -1,8 +1,183 @@ -""" -Temporary module to run legacy tests as a single pytest test case -as they're all plain asserts at module level. -""" +import pytest +import gitlab -def test_api_v4(gl): - from tools.functional import python_test_v4 + +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 len(namespace) != 0 + + 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 new file mode 100644 index 0000000..5a70650 --- /dev/null +++ b/tools/functional/api/test_groups.py @@ -0,0 +1,190 @@ +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 + + 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()) + 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 new file mode 100644 index 0000000..207d2e9 --- /dev/null +++ b/tools/functional/api/test_import_export.py @@ -0,0 +1,61 @@ +import time + + +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() + export.refresh() + + 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 new file mode 100644 index 0000000..ebff72b --- /dev/null +++ b/tools/functional/api/test_issues.py @@ -0,0 +1,89 @@ +import gitlab + + +def test_create_issue(project): + issue = project.issues.create({"title": "my issue 1"}) + issue2 = project.issues.create({"title": "my issue 2"}) + assert len(project.issues.list()) == 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): + label = 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 new file mode 100644 index 0000000..8c2ad54 --- /dev/null +++ b/tools/functional/api/test_merge_requests.py @@ -0,0 +1,96 @@ +import pytest + +import gitlab + + +def test_merge_requests(project): + project.files.create( + { + "file_path": "README.rst", + "branch": "master", + "content": "Initial content", + "commit_message": "Initial commit", + } + ) + + source_branch = "branch1" + branch = 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", + } + ) + mr = 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() + + +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() diff --git a/tools/functional/api/test_projects.py b/tools/functional/api/test_projects.py new file mode 100644 index 0000000..3e88c0c --- /dev/null +++ b/tools/functional/api/test_projects.py @@ -0,0 +1,298 @@ +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"}) + label = project.labels.list()[0] + assert len(project.labels.list()) == 1 + + label.new_name = "labelupdated" + label.save() + assert label.name == "labelupdated" + + label.subscribe() + assert label.subscribed == True + + label.unsubscribe() + assert label.subscribed == 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_releases(gl): + project = gl.projects.create( + {"name": "release-test-project", "initialize_with_readme": True} + ) + release_name = "Demo Release" + release_tag_name = "v1.2.3" + release_description = "release notes go here" + release = project.releases.create( + { + "name": release_name, + "tag_name": release_tag_name, + "description": release_description, + "ref": "master", + } + ) + 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 + + project.releases.delete(release_tag_name) + assert len(project.releases.list()) == 0 + project.delete() + + +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.files.create( + { + "file_path": "README", + "branch": "master", + "content": "Initial content", + "commit_message": "Initial commit", + } + ) + 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_repository.py b/tools/functional/api/test_repository.py new file mode 100644 index 0000000..a2bfd23 --- /dev/null +++ b/tools/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 len(tree) != 0 + 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()) + status = 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: + signature = 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 new file mode 100644 index 0000000..936fbfb --- /dev/null +++ b/tools/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/tools/functional/api/test_users.py b/tools/functional/api/test_users.py index f70da4a..e92c0fd 100644 --- a/tools/functional/api/test_users.py +++ b/tools/functional/api/test_users.py @@ -3,6 +3,158 @@ GitLab API: https://docs.gitlab.com/ee/api/users.html https://docs.gitlab.com/ee/api/users.html#delete-authentication-identity-from-user """ +import time +from pathlib import Path + +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() + wait_for_sidekiq() + + 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 len(projects) == 0 + + +def test_user_events_list(gl, user): + events = user.events.list() + assert len(events) == 0 + + +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): @@ -11,10 +163,8 @@ def test_user_identities(gl, user): 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] |
