Autoformat python tests (#5021)

main
Maxim Zhiltsov 4 years ago committed by GitHub
parent 6274bd11b6
commit 6654366021
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -17,7 +17,7 @@ jobs:
# If different modules use different Black configs,
# we need to run Black for each python component group separately.
# Otherwise, they all will use the same config.
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python/sdk" "tests/python/cli")
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python")
isValueIn () {
# Checks if a value is in an array

@ -17,7 +17,7 @@ jobs:
# If different modules use different isort configs,
# we need to run isort for each python component group separately.
# Otherwise, they all will use the same config.
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python/sdk" "tests/python/cli")
ENABLED_DIRS=("cvat-sdk" "cvat-cli" "tests/python")
isValueIn () {
# Checks if a value is in an array

@ -3,13 +3,17 @@
#
# SPDX-License-Identifier: MIT
import pytest
from http import HTTPStatus
import pytest
from shared.utils.config import server_get
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetAnalytics:
endpoint = 'analytics/app/kibana'
endpoint = "analytics/app/kibana"
def _test_can_see(self, user):
response = server_get(user, self.endpoint)
@ -20,12 +24,12 @@ class TestGetAnalytics:
assert response.status_code == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('privilege, is_allow', [
('admin', True), ('business', True),
('worker', False), ('user', False)
])
@pytest.mark.parametrize(
"privilege, is_allow",
[("admin", True), ("business", True), ("worker", False), ("user", False)],
)
def test_can_see(self, privilege, is_allow, find_users):
user = find_users(privilege=privilege)[0]['username']
user = find_users(privilege=privilege)[0]["username"]
if is_allow:
self._test_can_see(user)

@ -3,12 +3,13 @@
#
# SPDX-License-Identifier: MIT
from http import HTTPStatus
import re
from http import HTTPStatus
from shared.utils.config import server_get
class TestCachePolicy:
class TestCachePolicy:
@staticmethod
def _get_js_bundle_url(response):
match = re.search(r'<script.* src="(/assets/cvat-ui.\w+.min.js)".*></script>', response)
@ -17,22 +18,25 @@ class TestCachePolicy:
def _test_cache_policy_enabled(self, response):
assert response.status_code == HTTPStatus.OK
assert 'public' in response.headers['Cache-Control'] and 'max-age' in response.headers['Cache-Control']
assert (
"public" in response.headers["Cache-Control"]
and "max-age" in response.headers["Cache-Control"]
)
def _test_cache_policy_disabled(self, response):
assert response.status_code == HTTPStatus.OK
assert 'no-cache' in response.headers['Cache-Control']
assert "no-cache" in response.headers["Cache-Control"]
def test_index_not_cached(self, find_users):
user = find_users(privilege='user')[0]['username']
index_page_response = server_get(user, '/')
user = find_users(privilege="user")[0]["username"]
index_page_response = server_get(user, "/")
self._test_cache_policy_disabled(index_page_response)
def test_asset_cached(self, find_users):
user = find_users(privilege='user')[0]['username']
index_page_response = server_get(user, '/')
js_asset_url = self._get_js_bundle_url(index_page_response.content.decode('utf-8'))
user = find_users(privilege="user")[0]["username"]
index_page_response = server_get(user, "/")
js_asset_url = self._get_js_bundle_url(index_page_response.content.decode("utf-8"))
js_asset_response = server_get(user, js_asset_url)
self._test_cache_policy_enabled(js_asset_response)

@ -3,30 +3,46 @@
#
# SPDX-License-Identifier: MIT
import os.path as osp
import glob
import json
import os.path as osp
import pytest
from deepdiff import DeepDiff
from shared.utils import config
import pytest
@pytest.mark.usefixtures('dontchangedb')
class TestGetResources:
@pytest.mark.parametrize('path', glob.glob(osp.join(config.ASSETS_DIR, '*.json')))
@pytest.mark.usefixtures("dontchangedb")
class TestGetResources:
@pytest.mark.parametrize("path", glob.glob(osp.join(config.ASSETS_DIR, "*.json")))
def test_check_objects_integrity(self, path):
with open(path) as f:
endpoint = osp.basename(path).rsplit('.')[0]
if endpoint == 'annotations':
endpoint = osp.basename(path).rsplit(".")[0]
if endpoint == "annotations":
objects = json.load(f)
for jid, annotations in objects['job'].items():
response = config.get_method('admin1', f'jobs/{jid}/annotations').json()
assert DeepDiff(annotations, response, ignore_order=True,
exclude_paths="root['version']") == {}
for jid, annotations in objects["job"].items():
response = config.get_method("admin1", f"jobs/{jid}/annotations").json()
assert (
DeepDiff(
annotations,
response,
ignore_order=True,
exclude_paths="root['version']",
)
== {}
)
else:
response = config.get_method('admin1', endpoint, page_size='all')
response = config.get_method("admin1", endpoint, page_size="all")
json_objs = json.load(f)
resp_objs = response.json()
assert DeepDiff(json_objs, resp_objs, ignore_order=True,
exclude_regex_paths=r"root\['results'\]\[\d+\]\['last_login'\]") == {}
assert (
DeepDiff(
json_objs,
resp_objs,
ignore_order=True,
exclude_regex_paths=r"root\['results'\]\[\d+\]\['last_login'\]",
)
== {}
)

@ -3,58 +3,88 @@
#
# SPDX-License-Identifier: MIT
import pytest
from http import HTTPStatus
import pytest
from deepdiff import DeepDiff
from shared.utils.config import get_method, patch_method, post_method
@pytest.mark.usefixtures('dontchangedb')
class TestGetCloudStorage:
@pytest.mark.usefixtures("dontchangedb")
class TestGetCloudStorage:
def _test_can_see(self, user, storage_id, data, **kwargs):
response = get_method(user, f'cloudstorages/{storage_id}', **kwargs)
response = get_method(user, f"cloudstorages/{storage_id}", **kwargs)
response_data = response.json()
response_data = response_data.get('results', response_data)
response_data = response_data.get("results", response_data)
assert response.status_code == HTTPStatus.OK
assert DeepDiff(data, response_data, ignore_order=True,
exclude_paths="root['updated_date']") == {}
assert (
DeepDiff(data, response_data, ignore_order=True, exclude_paths="root['updated_date']")
== {}
)
def _test_cannot_see(self, user, storage_id, **kwargs):
response = get_method(user, f'cloudstorages/{storage_id}', **kwargs)
response = get_method(user, f"cloudstorages/{storage_id}", **kwargs)
assert response.status_code == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('storage_id', [1])
@pytest.mark.parametrize('group, is_owner, is_allow', [
('admin', False, True),
('business', False, False),
('user', True, True),
])
def test_sandbox_user_get_coud_storage(self, storage_id, group, is_owner, is_allow, users, cloud_storages):
org = ''
@pytest.mark.parametrize("storage_id", [1])
@pytest.mark.parametrize(
"group, is_owner, is_allow",
[
("admin", False, True),
("business", False, False),
("user", True, True),
],
)
def test_sandbox_user_get_coud_storage(
self, storage_id, group, is_owner, is_allow, users, cloud_storages
):
org = ""
cloud_storage = cloud_storages[storage_id]
username = cloud_storage['owner']['username'] if is_owner else \
next((u for u in users if group in u['groups'] and u['id'] != cloud_storage['owner']['id']))['username']
username = (
cloud_storage["owner"]["username"]
if is_owner
else next(
(
u
for u in users
if group in u["groups"] and u["id"] != cloud_storage["owner"]["id"]
)
)["username"]
)
if is_allow:
self._test_can_see(username, storage_id, cloud_storage, org=org)
else:
self._test_cannot_see(username, storage_id, org=org)
@pytest.mark.parametrize('org_id', [2])
@pytest.mark.parametrize('storage_id', [2])
@pytest.mark.parametrize('role, is_owner, is_allow', [
('worker', True, True),
('supervisor', False, True),
('worker', False, False),
])
def test_org_user_get_coud_storage(self, org_id, storage_id, role, is_owner, is_allow, find_users, cloud_storages):
@pytest.mark.parametrize("org_id", [2])
@pytest.mark.parametrize("storage_id", [2])
@pytest.mark.parametrize(
"role, is_owner, is_allow",
[
("worker", True, True),
("supervisor", False, True),
("worker", False, False),
],
)
def test_org_user_get_coud_storage(
self, org_id, storage_id, role, is_owner, is_allow, find_users, cloud_storages
):
cloud_storage = cloud_storages[storage_id]
username = cloud_storage['owner']['username'] if is_owner else \
next((u for u in find_users(role=role, org=org_id) if u['id'] != cloud_storage['owner']['id']))['username']
username = (
cloud_storage["owner"]["username"]
if is_owner
else next(
(
u
for u in find_users(role=role, org=org_id)
if u["id"] != cloud_storage["owner"]["id"]
)
)["username"]
)
if is_allow:
self._test_can_see(username, storage_id, cloud_storage, org_id=org_id)
@ -62,138 +92,189 @@ class TestGetCloudStorage:
self._test_cannot_see(username, storage_id, org_id=org_id)
@pytest.mark.usefixtures('changedb')
class TestPostCloudStorage():
@pytest.mark.usefixtures("changedb")
class TestPostCloudStorage:
_SPEC = {
'provider_type': 'AWS_S3_BUCKET',
'resource': 'test',
'display_name': 'Bucket',
'credentials_type': 'KEY_SECRET_KEY_PAIR',
'key': 'minio_access_key', 'secret_key': 'minio_secret_key',
'specific_attributes': 'endpoint_url=http://minio:9000',
'description': 'Some description',
'manifests': [
'manifest.jsonl'
],
"provider_type": "AWS_S3_BUCKET",
"resource": "test",
"display_name": "Bucket",
"credentials_type": "KEY_SECRET_KEY_PAIR",
"key": "minio_access_key",
"secret_key": "minio_secret_key",
"specific_attributes": "endpoint_url=http://minio:9000",
"description": "Some description",
"manifests": ["manifest.jsonl"],
}
_EXCLUDE_PATHS = [
f"root['{extra_field}']" for extra_field in {
f"root['{extra_field}']"
for extra_field in {
# unchanged fields
'created_date', 'id', 'organization', 'owner', 'updated_date',
"created_date",
"id",
"organization",
"owner",
"updated_date",
# credentials that server doesn't return
'key', 'secret_key',
}]
"key",
"secret_key",
}
]
def _test_can_create(self, user, spec, **kwargs):
response = post_method(user, 'cloudstorages', spec, **kwargs)
response = post_method(user, "cloudstorages", spec, **kwargs)
response_data = response.json()
response_data = response_data.get('results', response_data)
response_data = response_data.get("results", response_data)
assert response.status_code == HTTPStatus.CREATED
assert DeepDiff(self._SPEC, response_data, ignore_order=True,
exclude_paths=self._EXCLUDE_PATHS) == {}
assert (
DeepDiff(
self._SPEC, response_data, ignore_order=True, exclude_paths=self._EXCLUDE_PATHS
)
== {}
)
def _test_cannot_create(self, user, spec, **kwargs):
response = post_method(user, 'cloudstorages', spec, **kwargs)
response = post_method(user, "cloudstorages", spec, **kwargs)
assert response.status_code == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('group, is_allow', [
('user', True), ('worker', False)
])
@pytest.mark.parametrize("group, is_allow", [("user", True), ("worker", False)])
def test_sandbox_user_create_cloud_storage(self, group, is_allow, users):
org = ''
username = [u for u in users if group in u['groups']][0]['username']
org = ""
username = [u for u in users if group in u["groups"]][0]["username"]
if is_allow:
self._test_can_create(username, self._SPEC, org=org)
else:
self._test_cannot_create(username, self._SPEC, org=org)
@pytest.mark.parametrize('org_id', [2])
@pytest.mark.parametrize('role, is_allow', [
('owner', True), ('maintainer', True),
('worker', False), ('supervisor', False),
])
@pytest.mark.parametrize("org_id", [2])
@pytest.mark.parametrize(
"role, is_allow",
[
("owner", True),
("maintainer", True),
("worker", False),
("supervisor", False),
],
)
def test_org_user_create_coud_storage(self, org_id, role, is_allow, find_users):
username = find_users(role=role, org=org_id)[0]['username']
username = find_users(role=role, org=org_id)[0]["username"]
if is_allow:
self._test_can_create(username, self._SPEC, org_id=org_id)
else:
self._test_cannot_create(username, self._SPEC, org_id=org_id)
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchCloudStorage:
_SPEC = {
'display_name': 'New display name',
'description': 'New description',
'manifests': [
'manifest_1.jsonl',
'manifest_2.jsonl',
"display_name": "New display name",
"description": "New description",
"manifests": [
"manifest_1.jsonl",
"manifest_2.jsonl",
],
}
_PRIVATE_BUCKET_SPEC = {
'display_name': 'New display name',
'description': 'New description',
'manifests': [
'sub/manifest_1.jsonl',
'sub/manifest_2.jsonl',
"display_name": "New display name",
"description": "New description",
"manifests": [
"sub/manifest_1.jsonl",
"sub/manifest_2.jsonl",
],
}
_EXCLUDE_PATHS = [
f"root['{extra_field}']" for extra_field in {
f"root['{extra_field}']"
for extra_field in {
# unchanged fields
'created_date', 'credentials_type', 'id', 'organization', 'owner',
'provider_type', 'resource', 'specific_attributes', 'updated_date',
}]
"created_date",
"credentials_type",
"id",
"organization",
"owner",
"provider_type",
"resource",
"specific_attributes",
"updated_date",
}
]
def _test_can_update(self, user, storage_id, spec, **kwargs):
response = patch_method(user, f'cloudstorages/{storage_id}', spec, **kwargs)
response = patch_method(user, f"cloudstorages/{storage_id}", spec, **kwargs)
response_data = response.json()
response_data = response_data.get('results', response_data)
response_data = response_data.get("results", response_data)
assert response.status_code == HTTPStatus.OK
assert DeepDiff(spec, response_data, ignore_order=True,
exclude_paths=self._EXCLUDE_PATHS) == {}
assert (
DeepDiff(spec, response_data, ignore_order=True, exclude_paths=self._EXCLUDE_PATHS)
== {}
)
assert response.status_code == HTTPStatus.OK
def _test_cannot_update(self, user, storage_id, spec, **kwargs):
response = patch_method(user, f'cloudstorages/{storage_id}', spec, **kwargs)
response = patch_method(user, f"cloudstorages/{storage_id}", spec, **kwargs)
assert response.status_code == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('storage_id', [1])
@pytest.mark.parametrize('group, is_owner, is_allow', [
('admin', False, True),
('business', False, False),
('worker', True, True),
])
def test_sandbox_user_update_cloud_storage(self, storage_id, group, is_owner, is_allow, users, cloud_storages):
org = ''
@pytest.mark.parametrize("storage_id", [1])
@pytest.mark.parametrize(
"group, is_owner, is_allow",
[
("admin", False, True),
("business", False, False),
("worker", True, True),
],
)
def test_sandbox_user_update_cloud_storage(
self, storage_id, group, is_owner, is_allow, users, cloud_storages
):
org = ""
cloud_storage = cloud_storages[storage_id]
username = cloud_storage['owner']['username'] if is_owner else \
next((u for u in users if group in u['groups'] and u['id'] != cloud_storage['owner']['id']))['username']
username = (
cloud_storage["owner"]["username"]
if is_owner
else next(
(
u
for u in users
if group in u["groups"] and u["id"] != cloud_storage["owner"]["id"]
)
)["username"]
)
if is_allow:
self._test_can_update(username, storage_id, self._SPEC, org=org)
else:
self._test_cannot_update(username, storage_id, self._SPEC, org=org)
@pytest.mark.parametrize('org_id', [2])
@pytest.mark.parametrize('storage_id', [2])
@pytest.mark.parametrize('role, is_owner, is_allow', [
('worker', True, True),
('maintainer', False, True),
('supervisor', False, False),
])
def test_org_user_update_cloud_storage(self, org_id, storage_id, role, is_owner, is_allow, find_users, cloud_storages):
@pytest.mark.parametrize("org_id", [2])
@pytest.mark.parametrize("storage_id", [2])
@pytest.mark.parametrize(
"role, is_owner, is_allow",
[
("worker", True, True),
("maintainer", False, True),
("supervisor", False, False),
],
)
def test_org_user_update_cloud_storage(
self, org_id, storage_id, role, is_owner, is_allow, find_users, cloud_storages
):
cloud_storage = cloud_storages[storage_id]
username = cloud_storage['owner']['username'] if is_owner else \
next((u for u in find_users(role=role, org=org_id) if u['id'] != cloud_storage['owner']['id']))['username']
username = (
cloud_storage["owner"]["username"]
if is_owner
else next(
(
u
for u in find_users(role=role, org=org_id)
if u["id"] != cloud_storage["owner"]["id"]
)
)["username"]
)
if is_allow:
self._test_can_update(username, storage_id, self._PRIVATE_BUCKET_SPEC, org_id=org_id)

@ -4,71 +4,83 @@
# SPDX-License-Identifier: MIT
from http import HTTPStatus
import pytest
from shared.utils.config import post_method
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestCreateInvitations:
def _test_post_invitation_201(self, user, data, invitee, **kwargs):
response = post_method(user, 'invitations', data, **kwargs)
response = post_method(user, "invitations", data, **kwargs)
assert response.status_code == HTTPStatus.CREATED
assert data['role'] == response.json()['role']
assert invitee['id'] == response.json()['user']['id']
assert kwargs['org_id'] == response.json()['organization']
assert data["role"] == response.json()["role"]
assert invitee["id"] == response.json()["user"]["id"]
assert kwargs["org_id"] == response.json()["organization"]
def _test_post_invitation_403(self, user, data, **kwargs):
response = post_method(user, 'invitations', data, **kwargs)
response = post_method(user, "invitations", data, **kwargs)
assert response.status_code == HTTPStatus.FORBIDDEN
@staticmethod
def get_non_member_users(memberships, users):
organization_users = set(m['user']['id'] for m in memberships if m['user'] is not None)
non_member_users = [u for u in users if u['id'] not in organization_users]
organization_users = set(m["user"]["id"] for m in memberships if m["user"] is not None)
non_member_users = [u for u in users if u["id"] not in organization_users]
return non_member_users
@staticmethod
def get_member(role, memberships, org_id):
member = [m['user'] for m in memberships if m['role'] == role and
m['organization'] == org_id and m['user'] is not None][0]
member = [
m["user"]
for m in memberships
if m["role"] == role and m["organization"] == org_id and m["user"] is not None
][0]
return member
@pytest.mark.parametrize('org_id', [2])
@pytest.mark.parametrize('org_role', ['worker', 'supervisor', 'maintainer', 'owner'])
def test_create_invitation(self, organizations, memberships, users,
org_id, org_role):
@pytest.mark.parametrize("org_id", [2])
@pytest.mark.parametrize("org_role", ["worker", "supervisor", "maintainer", "owner"])
def test_create_invitation(self, organizations, memberships, users, org_id, org_role):
member = self.get_member(org_role, memberships, org_id)
non_member_users = self.get_non_member_users(memberships, users)
if org_role in ['worker', 'supervisor']:
for invitee_role in ['worker', 'supervisor', 'maintainer', 'owner']:
self._test_post_invitation_403(member['username'], {
'role': invitee_role,
'email': non_member_users[0]['email']
}, org_id=org_id)
if org_role in ["worker", "supervisor"]:
for invitee_role in ["worker", "supervisor", "maintainer", "owner"]:
self._test_post_invitation_403(
member["username"],
{"role": invitee_role, "email": non_member_users[0]["email"]},
org_id=org_id,
)
else:
for idx, invitee_role in enumerate(['worker', 'supervisor']):
self._test_post_invitation_201(member['username'], {
'role': invitee_role,
'email': non_member_users[idx]['email']
}, non_member_users[idx], org_id=org_id)
for idx, invitee_role in enumerate(["worker", "supervisor"]):
self._test_post_invitation_201(
member["username"],
{"role": invitee_role, "email": non_member_users[idx]["email"]},
non_member_users[idx],
org_id=org_id,
)
# only the owner can invite a maintainer
if org_role == 'owner':
self._test_post_invitation_201(member['username'], {
'role': 'maintainer',
'email': non_member_users[2]['email']
}, non_member_users[2], org_id=org_id)
if org_role == "owner":
self._test_post_invitation_201(
member["username"],
{"role": "maintainer", "email": non_member_users[2]["email"]},
non_member_users[2],
org_id=org_id,
)
else:
self._test_post_invitation_403(member['username'], {
'role': 'maintainer',
'email': non_member_users[3]['email']
}, org_id=org_id)
self._test_post_invitation_403(
member["username"],
{"role": "maintainer", "email": non_member_users[3]["email"]},
org_id=org_id,
)
# nobody can invite an owner
self._test_post_invitation_403(member['username'], {
'role': 'owner',
'email': non_member_users[4]['email']
}, org_id=org_id)
self._test_post_invitation_403(
member["username"],
{"role": "owner", "email": non_member_users[4]["email"]},
org_id=org_id,
)

@ -9,9 +9,8 @@ from http import HTTPStatus
import pytest
from cvat_sdk import models
from deepdiff import DeepDiff
from cvat_sdk.api_client import exceptions
from deepdiff import DeepDiff
from shared.utils.config import make_api_client
@ -227,11 +226,13 @@ class TestPatchIssues:
data = request_data(issue_id)
self._test_check_response(username, issue_id, data, is_allow, org_id=org)
@pytest.mark.xfail(raises=exceptions.ServiceException,
reason="server bug, https://github.com/cvat-ai/cvat/issues/122")
@pytest.mark.xfail(
raises=exceptions.ServiceException,
reason="server bug, https://github.com/cvat-ai/cvat/issues/122",
)
def test_cant_update_message(self, admin_user: str, issues_by_org):
org = 2
issue_id = issues_by_org[org][0]['id']
issue_id = issues_by_org[org][0]["id"]
with make_api_client(admin_user) as client:
client.issues_api.partial_update(

@ -3,75 +3,86 @@
#
# SPDX-License-Identifier: MIT
from http import HTTPStatus
import json
from copy import deepcopy
from http import HTTPStatus
from typing import List
import pytest
from cvat_sdk.core.helpers import get_paginated_collection
from deepdiff import DeepDiff
import pytest
from copy import deepcopy
from shared.utils.config import make_api_client
from .utils import export_dataset
def get_job_staff(job, tasks, projects):
job_staff = []
job_staff.append(job['assignee'])
tid = job['task_id']
job_staff.append(tasks[tid]['owner'])
job_staff.append(tasks[tid]['assignee'])
job_staff.append(job["assignee"])
tid = job["task_id"]
job_staff.append(tasks[tid]["owner"])
job_staff.append(tasks[tid]["assignee"])
pid = job['project_id']
pid = job["project_id"]
if pid:
job_staff.append(projects[pid]['owner'])
job_staff.append(projects[pid]['assignee'])
job_staff = set(u['id'] for u in job_staff if u is not None)
job_staff.append(projects[pid]["owner"])
job_staff.append(projects[pid]["assignee"])
job_staff = set(u["id"] for u in job_staff if u is not None)
return job_staff
def filter_jobs(jobs, tasks, org):
if org is None:
kwargs = {}
jobs = jobs.raw
elif org == '':
kwargs = {'org': ''}
jobs = [job for job in jobs
if tasks[job['task_id']]['organization'] is None]
elif org == "":
kwargs = {"org": ""}
jobs = [job for job in jobs if tasks[job["task_id"]]["organization"] is None]
else:
kwargs = {'org_id': org}
jobs = [job for job in jobs
if tasks[job['task_id']]['organization'] == org]
kwargs = {"org_id": org}
jobs = [job for job in jobs if tasks[job["task_id"]]["organization"] == org]
return jobs, kwargs
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetJobs:
def _test_get_job_200(self, user, jid, data, **kwargs):
with make_api_client(user) as client:
(_, response) = client.jobs_api.retrieve(jid, **kwargs)
assert response.status == HTTPStatus.OK
assert DeepDiff(data, json.loads(response.data), exclude_paths="root['updated_date']",
ignore_order=True) == {}
assert (
DeepDiff(
data,
json.loads(response.data),
exclude_paths="root['updated_date']",
ignore_order=True,
)
== {}
)
def _test_get_job_403(self, user, jid, **kwargs):
with make_api_client(user) as client:
(_, response) = client.jobs_api.retrieve(jid, **kwargs,
_check_status=False, _parse_response=False)
(_, response) = client.jobs_api.retrieve(
jid, **kwargs, _check_status=False, _parse_response=False
)
assert response.status == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('org', [None, '', 1, 2])
@pytest.mark.parametrize("org", [None, "", 1, 2])
def test_admin_get_job(self, jobs, tasks, org):
jobs, kwargs = filter_jobs(jobs, tasks, org)
# keep only the reasonable amount of jobs
for job in jobs[:8]:
self._test_get_job_200('admin2', job['id'], job, **kwargs)
self._test_get_job_200("admin2", job["id"], job, **kwargs)
@pytest.mark.parametrize('org_id', ['', None, 1, 2])
@pytest.mark.parametrize('groups', [['business'], ['user'], ['worker'], []])
def test_non_admin_get_job(self, org_id, groups, users, jobs, tasks, projects,
org_staff):
@pytest.mark.parametrize("org_id", ["", None, 1, 2])
@pytest.mark.parametrize("groups", [["business"], ["user"], ["worker"], []])
def test_non_admin_get_job(self, org_id, groups, users, jobs, tasks, projects, org_staff):
# keep the reasonable amount of users and jobs
users = [u for u in users if u['groups'] == groups][:4]
users = [u for u in users if u["groups"] == groups][:4]
jobs, kwargs = filter_jobs(jobs, tasks, org_id)
org_staff = org_staff(org_id)
@ -80,36 +91,42 @@ class TestGetJobs:
# check if the specific user in job_staff to see the job
for user in users:
if user['id'] in job_staff | org_staff:
self._test_get_job_200(user['username'], job['id'], job, **kwargs)
if user["id"] in job_staff | org_staff:
self._test_get_job_200(user["username"], job["id"], job, **kwargs)
else:
self._test_get_job_403(user['username'], job['id'], **kwargs)
self._test_get_job_403(user["username"], job["id"], **kwargs)
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestListJobs:
def _test_list_jobs_200(self, user, data, **kwargs):
with make_api_client(user) as client:
results = get_paginated_collection(client.jobs_api.list_endpoint,
return_json=True, **kwargs)
assert DeepDiff(data, results, exclude_paths="root['updated_date']",
ignore_order=True) == {}
results = get_paginated_collection(
client.jobs_api.list_endpoint, return_json=True, **kwargs
)
assert (
DeepDiff(data, results, exclude_paths="root['updated_date']", ignore_order=True)
== {}
)
def _test_list_jobs_403(self, user, **kwargs):
with make_api_client(user) as client:
(_, response) = client.jobs_api.list(**kwargs,
_check_status=False, _parse_response=False)
(_, response) = client.jobs_api.list(
**kwargs, _check_status=False, _parse_response=False
)
assert response.status == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('org', [None, '', 1, 2])
@pytest.mark.parametrize("org", [None, "", 1, 2])
def test_admin_list_jobs(self, jobs, tasks, org):
jobs, kwargs = filter_jobs(jobs, tasks, org)
self._test_list_jobs_200('admin1', jobs, **kwargs)
@pytest.mark.parametrize('org_id', ['', None, 1, 2])
@pytest.mark.parametrize('groups', [['business'], ['user'], ['worker'], []])
def test_non_admin_list_jobs(self, org_id, groups, users, jobs, tasks,
projects, org_staff, is_org_member):
users = [u for u in users if u['groups'] == groups][:2]
self._test_list_jobs_200("admin1", jobs, **kwargs)
@pytest.mark.parametrize("org_id", ["", None, 1, 2])
@pytest.mark.parametrize("groups", [["business"], ["user"], ["worker"], []])
def test_non_admin_list_jobs(
self, org_id, groups, users, jobs, tasks, projects, org_staff, is_org_member
):
users = [u for u in users if u["groups"] == groups][:2]
jobs, kwargs = filter_jobs(jobs, tasks, org_id)
org_staff = org_staff(org_id)
@ -117,14 +134,15 @@ class TestListJobs:
user_jobs = []
for job in jobs:
job_staff = get_job_staff(job, tasks, projects)
if user['id'] in job_staff | org_staff:
if user["id"] in job_staff | org_staff:
user_jobs.append(job)
if is_org_member(user['id'], org_id):
self._test_list_jobs_200(user['username'], user_jobs, **kwargs)
if is_org_member(user["id"], org_id):
self._test_list_jobs_200(user["username"], user_jobs, **kwargs)
else:
self._test_list_jobs_403(user['username'], **kwargs)
self._test_list_jobs_403(user["username"], **kwargs)
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetAnnotations:
def _test_get_job_annotations_200(self, user, jid, data, **kwargs):
with make_api_client(user) as client:
@ -132,112 +150,191 @@ class TestGetAnnotations:
assert response.status == HTTPStatus.OK
response_data = json.loads(response.data)
response_data['shapes'] = sorted(response_data['shapes'], key=lambda a: a['id'])
assert DeepDiff(data, response_data,
exclude_regex_paths=r"root\['version|updated_date'\]") == {}
response_data["shapes"] = sorted(response_data["shapes"], key=lambda a: a["id"])
assert (
DeepDiff(data, response_data, exclude_regex_paths=r"root\['version|updated_date'\]")
== {}
)
def _test_get_job_annotations_403(self, user, jid, **kwargs):
with make_api_client(user) as client:
(_, response) = client.jobs_api.retrieve_annotations(jid, **kwargs,
_check_status=False, _parse_response=False)
(_, response) = client.jobs_api.retrieve_annotations(
jid, **kwargs, _check_status=False, _parse_response=False
)
assert response.status == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('org', [''])
@pytest.mark.parametrize('groups, job_staff, expect_success', [
(['admin'], True, True), (['admin'], False, True),
(['business'], True, True), (['business'], False, False),
(['worker'], True, True), (['worker'], False, False),
(['user'], True, True), (['user'], False, False)
])
def test_user_get_job_annotations(self, org, groups, job_staff,
expect_success, users, jobs, tasks, annotations, find_job_staff_user):
users = [u for u in users if u['groups'] == groups]
@pytest.mark.parametrize("org", [""])
@pytest.mark.parametrize(
"groups, job_staff, expect_success",
[
(["admin"], True, True),
(["admin"], False, True),
(["business"], True, True),
(["business"], False, False),
(["worker"], True, True),
(["worker"], False, False),
(["user"], True, True),
(["user"], False, False),
],
)
def test_user_get_job_annotations(
self,
org,
groups,
job_staff,
expect_success,
users,
jobs,
tasks,
annotations,
find_job_staff_user,
):
users = [u for u in users if u["groups"] == groups]
jobs, kwargs = filter_jobs(jobs, tasks, org)
username, job_id = find_job_staff_user(jobs, users, job_staff)
if expect_success:
self._test_get_job_annotations_200(username,
job_id, annotations['job'][str(job_id)], **kwargs)
self._test_get_job_annotations_200(
username, job_id, annotations["job"][str(job_id)], **kwargs
)
else:
self._test_get_job_annotations_403(username, job_id, **kwargs)
@pytest.mark.parametrize('org', [2])
@pytest.mark.parametrize('role, job_staff, expect_success', [
('owner', True, True), ('owner', False, True),
('maintainer', True, True), ('maintainer', False, True),
('supervisor', True, True), ('supervisor', False, False),
('worker', True, True), ('worker', False, False),
])
def test_member_get_job_annotations(self, org, role, job_staff, expect_success,
jobs, tasks, find_job_staff_user, annotations, find_users):
@pytest.mark.parametrize("org", [2])
@pytest.mark.parametrize(
"role, job_staff, expect_success",
[
("owner", True, True),
("owner", False, True),
("maintainer", True, True),
("maintainer", False, True),
("supervisor", True, True),
("supervisor", False, False),
("worker", True, True),
("worker", False, False),
],
)
def test_member_get_job_annotations(
self,
org,
role,
job_staff,
expect_success,
jobs,
tasks,
find_job_staff_user,
annotations,
find_users,
):
users = find_users(org=org, role=role)
jobs, kwargs = filter_jobs(jobs, tasks, org)
username, jid = find_job_staff_user(jobs, users, job_staff)
if expect_success:
data = annotations['job'][str(jid)]
data['shapes'] = sorted(data['shapes'], key=lambda a: a['id'])
data = annotations["job"][str(jid)]
data["shapes"] = sorted(data["shapes"], key=lambda a: a["id"])
self._test_get_job_annotations_200(username, jid, data, **kwargs)
else:
self._test_get_job_annotations_403(username, jid, **kwargs)
@pytest.mark.parametrize('org', [1])
@pytest.mark.parametrize('privilege, expect_success', [
('admin', True), ('business', False), ('worker', False), ('user', False)
])
def test_non_member_get_job_annotations(self, org, privilege, expect_success,
jobs, tasks, find_job_staff_user, annotations, find_users):
@pytest.mark.parametrize("org", [1])
@pytest.mark.parametrize(
"privilege, expect_success",
[("admin", True), ("business", False), ("worker", False), ("user", False)],
)
def test_non_member_get_job_annotations(
self,
org,
privilege,
expect_success,
jobs,
tasks,
find_job_staff_user,
annotations,
find_users,
):
users = find_users(privilege=privilege, exclude_org=org)
jobs, kwargs = filter_jobs(jobs, tasks, org)
username, job_id = find_job_staff_user(jobs, users, False)
kwargs = {'org_id': org}
kwargs = {"org_id": org}
if expect_success:
self._test_get_job_annotations_200(username,
job_id, annotations['job'][str(job_id)], **kwargs)
self._test_get_job_annotations_200(
username, job_id, annotations["job"][str(job_id)], **kwargs
)
else:
self._test_get_job_annotations_403(username, job_id, **kwargs)
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchJobAnnotations:
def _check_respone(self, username, jid, expect_success, data=None, org=None):
kwargs = {}
if org is not None:
if isinstance(org, str):
kwargs['org'] = org
kwargs["org"] = org
else:
kwargs['org_id'] = org
kwargs["org_id"] = org
with make_api_client(username) as client:
(_, response) = client.jobs_api.partial_update_annotations(id=jid,
patched_labeled_data_request=deepcopy(data), action='update', **kwargs,
_parse_response=expect_success, _check_status=expect_success)
(_, response) = client.jobs_api.partial_update_annotations(
id=jid,
patched_labeled_data_request=deepcopy(data),
action="update",
**kwargs,
_parse_response=expect_success,
_check_status=expect_success,
)
if expect_success:
assert response.status == HTTPStatus.OK
assert DeepDiff(data, json.loads(response.data),
exclude_regex_paths=r"root\['version|updated_date'\]") == {}
assert (
DeepDiff(
data,
json.loads(response.data),
exclude_regex_paths=r"root\['version|updated_date'\]",
)
== {}
)
else:
assert response.status == HTTPStatus.FORBIDDEN
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def request_data(self, annotations):
def get_data(jid):
data = deepcopy(annotations['job'][str(jid)])
data['shapes'][0].update({'points': [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]})
data['version'] += 1
data = deepcopy(annotations["job"][str(jid)])
data["shapes"][0].update({"points": [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]})
data["version"] += 1
return data
return get_data
@pytest.mark.parametrize('org', [2])
@pytest.mark.parametrize('role, job_staff, expect_success', [
('maintainer', False, True), ('owner', False, True),
('supervisor', False, False), ('worker', False, False),
('maintainer', True, True), ('owner', True, True),
('supervisor', True, True), ('worker', True, True)
])
def test_member_update_job_annotations(self, org, role, job_staff, expect_success,
find_job_staff_user, find_users, request_data, jobs_by_org, filter_jobs_with_shapes):
@pytest.mark.parametrize("org", [2])
@pytest.mark.parametrize(
"role, job_staff, expect_success",
[
("maintainer", False, True),
("owner", False, True),
("supervisor", False, False),
("worker", False, False),
("maintainer", True, True),
("owner", True, True),
("supervisor", True, True),
("worker", True, True),
],
)
def test_member_update_job_annotations(
self,
org,
role,
job_staff,
expect_success,
find_job_staff_user,
find_users,
request_data,
jobs_by_org,
filter_jobs_with_shapes,
):
users = find_users(role=role, org=org)
jobs = jobs_by_org[org]
filtered_jobs = filter_jobs_with_shapes(jobs)
@ -246,12 +343,22 @@ class TestPatchJobAnnotations:
data = request_data(jid)
self._check_respone(username, jid, expect_success, data, org=org)
@pytest.mark.parametrize('org', [2])
@pytest.mark.parametrize('privilege, expect_success', [
('admin', True), ('business', False), ('worker', False), ('user', False)
])
def test_non_member_update_job_annotations(self, org, privilege, expect_success,
find_job_staff_user, find_users, request_data, jobs_by_org, filter_jobs_with_shapes):
@pytest.mark.parametrize("org", [2])
@pytest.mark.parametrize(
"privilege, expect_success",
[("admin", True), ("business", False), ("worker", False), ("user", False)],
)
def test_non_member_update_job_annotations(
self,
org,
privilege,
expect_success,
find_job_staff_user,
find_users,
request_data,
jobs_by_org,
filter_jobs_with_shapes,
):
users = find_users(privilege=privilege, exclude_org=org)
jobs = jobs_by_org[org]
filtered_jobs = filter_jobs_with_shapes(jobs)
@ -260,15 +367,32 @@ class TestPatchJobAnnotations:
data = request_data(jid)
self._check_respone(username, jid, expect_success, data, org=org)
@pytest.mark.parametrize('org', [''])
@pytest.mark.parametrize('privilege, job_staff, expect_success', [
('admin', True, True), ('admin', False, True),
('business', True, True), ('business', False, False),
('worker', True, True), ('worker', False, False),
('user', True, True), ('user', False, False)
])
def test_user_update_job_annotations(self, org, privilege, job_staff, expect_success,
find_job_staff_user, find_users, request_data, jobs_by_org, filter_jobs_with_shapes):
@pytest.mark.parametrize("org", [""])
@pytest.mark.parametrize(
"privilege, job_staff, expect_success",
[
("admin", True, True),
("admin", False, True),
("business", True, True),
("business", False, False),
("worker", True, True),
("worker", False, False),
("user", True, True),
("user", False, False),
],
)
def test_user_update_job_annotations(
self,
org,
privilege,
job_staff,
expect_success,
find_job_staff_user,
find_users,
request_data,
jobs_by_org,
filter_jobs_with_shapes,
):
users = find_users(privilege=privilege)
jobs = jobs_by_org[org]
filtered_jobs = filter_jobs_with_shapes(jobs)
@ -277,62 +401,95 @@ class TestPatchJobAnnotations:
data = request_data(jid)
self._check_respone(username, jid, expect_success, data, org=org)
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchJob:
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def find_task_staff_user(self, is_task_staff):
def find(jobs, users, is_staff):
for job in jobs:
for user in users:
if is_staff == is_task_staff(user['id'], job['task_id']):
return user, job['id']
if is_staff == is_task_staff(user["id"], job["task_id"]):
return user, job["id"]
return None, None
return find
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def expected_data(self, jobs, users):
keys = ['url', 'id', 'username', 'first_name', 'last_name']
keys = ["url", "id", "username", "first_name", "last_name"]
def find(job_id, assignee_id):
data = deepcopy(jobs[job_id])
data['assignee'] = dict(filter(lambda a: a[0] in keys,
users[assignee_id].items()))
data["assignee"] = dict(filter(lambda a: a[0] in keys, users[assignee_id].items()))
return data
return find
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def new_assignee(self, jobs, tasks, assignee_id, org_staff):
def find_new_assignee(jid, user_id):
members = org_staff(tasks[jobs[jid]['task_id']]['organization'])
members = org_staff(tasks[jobs[jid]["task_id"]]["organization"])
members -= {assignee_id(jobs[jid]), user_id}
return members.pop()
return find_new_assignee
@pytest.mark.parametrize('org', [2])
@pytest.mark.parametrize('role, task_staff, expect_success', [
('maintainer', False, True), ('owner', False, True),
('supervisor', False, False), ('worker', False, False),
('maintainer', True, True), ('owner', True, True),
('supervisor', True, True), ('worker', True, True)
])
def test_member_update_job_assignee(self, org, role, task_staff, expect_success,
find_task_staff_user, find_users, jobs_by_org, new_assignee, expected_data):
@pytest.mark.parametrize("org", [2])
@pytest.mark.parametrize(
"role, task_staff, expect_success",
[
("maintainer", False, True),
("owner", False, True),
("supervisor", False, False),
("worker", False, False),
("maintainer", True, True),
("owner", True, True),
("supervisor", True, True),
("worker", True, True),
],
)
def test_member_update_job_assignee(
self,
org,
role,
task_staff,
expect_success,
find_task_staff_user,
find_users,
jobs_by_org,
new_assignee,
expected_data,
):
users, jobs = find_users(role=role, org=org), jobs_by_org[org]
user, jid = find_task_staff_user(jobs, users, task_staff)
assignee = new_assignee(jid, user['id'])
with make_api_client(user['username']) as client:
(_, response) = client.jobs_api.partial_update(id=jid,
patched_job_write_request={'assignee': assignee}, org_id=org,
_parse_response=expect_success, _check_status=expect_success)
assignee = new_assignee(jid, user["id"])
with make_api_client(user["username"]) as client:
(_, response) = client.jobs_api.partial_update(
id=jid,
patched_job_write_request={"assignee": assignee},
org_id=org,
_parse_response=expect_success,
_check_status=expect_success,
)
if expect_success:
assert response.status == HTTPStatus.OK
assert DeepDiff(expected_data(jid, assignee), json.loads(response.data),
exclude_paths="root['updated_date']", ignore_order=True) == {}
assert (
DeepDiff(
expected_data(jid, assignee),
json.loads(response.data),
exclude_paths="root['updated_date']",
ignore_order=True,
)
== {}
)
else:
assert response.status == HTTPStatus.FORBIDDEN
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestJobDataset:
def _export_dataset(self, username, jid, **kwargs):
with make_api_client(username) as api_client:
@ -340,15 +497,16 @@ class TestJobDataset:
def _export_annotations(self, username, jid, **kwargs):
with make_api_client(username) as api_client:
return export_dataset(api_client.jobs_api.retrieve_annotations_endpoint,
id=jid, **kwargs)
return export_dataset(
api_client.jobs_api.retrieve_annotations_endpoint, id=jid, **kwargs
)
def test_can_export_dataset(self, admin_user: str, jobs_with_shapes: List):
job = jobs_with_shapes[0]
response = self._export_dataset(admin_user, job['id'], format='CVAT for images 1.1')
response = self._export_dataset(admin_user, job["id"], format="CVAT for images 1.1")
assert response.data
def test_can_export_annotations(self, admin_user: str, jobs_with_shapes: List):
job = jobs_with_shapes[0]
response = self._export_annotations(admin_user, job['id'], format='CVAT for images 1.1')
response = self._export_annotations(admin_user, job["id"], format="CVAT for images 1.1")
assert response.data

@ -3,77 +3,84 @@
#
# SPDX-License-Identifier: MIT
import pytest
from http import HTTPStatus
import pytest
from deepdiff import DeepDiff
from shared.utils.config import get_method, patch_method
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetMemberships:
def _test_can_see_memberships(self, user, data, **kwargs):
response = get_method(user, 'memberships', **kwargs)
response = get_method(user, "memberships", **kwargs)
assert response.status_code == HTTPStatus.OK
assert DeepDiff(data, response.json()['results']) == {}
assert DeepDiff(data, response.json()["results"]) == {}
def _test_cannot_see_memberships(self, user, **kwargs):
response = get_method(user, 'memberships', **kwargs)
response = get_method(user, "memberships", **kwargs)
assert response.status_code == HTTPStatus.FORBIDDEN
def test_admin_can_see_all_memberships(self, memberships):
self._test_can_see_memberships('admin2', memberships.raw, page_size='all')
self._test_can_see_memberships("admin2", memberships.raw, page_size="all")
def test_non_admin_can_see_only_self_memberships(self, memberships):
non_admins= ['business1', 'user1', 'dummy1','worker2']
non_admins = ["business1", "user1", "dummy1", "worker2"]
for username in non_admins:
data = [obj for obj in memberships
if obj['user']['username'] == username]
data = [obj for obj in memberships if obj["user"]["username"] == username]
self._test_can_see_memberships(username, data)
def test_all_members_can_see_other_members_membership(self, memberships):
data = [obj for obj in memberships if obj['organization'] == 1]
data = [obj for obj in memberships if obj["organization"] == 1]
for membership in data:
self._test_can_see_memberships(membership['user']['username'],
data, org_id=1)
self._test_can_see_memberships(membership["user"]["username"], data, org_id=1)
def test_non_members_cannot_see_members_membership(self):
non_org1_users = ['user2', 'worker3']
non_org1_users = ["user2", "worker3"]
for user in non_org1_users:
self._test_cannot_see_memberships(user, org_id=1)
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchMemberships:
_ORG = 2
def _test_can_change_membership(self, user, membership_id, new_role):
response = patch_method(user, f"memberships/{membership_id}",
{'role': new_role}, org_id=self._ORG)
response = patch_method(
user, f"memberships/{membership_id}", {"role": new_role}, org_id=self._ORG
)
assert response.status_code == HTTPStatus.OK
assert response.json()['role'] == new_role
assert response.json()["role"] == new_role
def _test_cannot_change_membership(self, user, membership_id, new_role):
response = patch_method(user, f"memberships/{membership_id}",
{'role': new_role}, org_id=self._ORG)
response = patch_method(
user, f"memberships/{membership_id}", {"role": new_role}, org_id=self._ORG
)
assert response.status_code == HTTPStatus.FORBIDDEN
@pytest.mark.parametrize('who, whom, new_role, is_allow', [
('supervisor', 'worker', 'supervisor', False),
('supervisor', 'maintainer', 'supervisor', False),
('worker', 'supervisor', 'worker', False),
('worker', 'maintainer', 'worker', False),
('maintainer', 'maintainer', 'worker', False),
('maintainer', 'supervisor', 'worker', True),
('maintainer', 'worker', 'supervisor', True),
('owner', 'maintainer', 'worker', True),
('owner', 'supervisor', 'worker', True),
('owner', 'worker', 'supervisor', True),
])
@pytest.mark.parametrize(
"who, whom, new_role, is_allow",
[
("supervisor", "worker", "supervisor", False),
("supervisor", "maintainer", "supervisor", False),
("worker", "supervisor", "worker", False),
("worker", "maintainer", "worker", False),
("maintainer", "maintainer", "worker", False),
("maintainer", "supervisor", "worker", True),
("maintainer", "worker", "supervisor", True),
("owner", "maintainer", "worker", True),
("owner", "supervisor", "worker", True),
("owner", "worker", "supervisor", True),
],
)
def test_user_can_change_role_of_member(self, who, whom, new_role, is_allow, find_users):
user = find_users(org=self._ORG, role=who)[0]['username']
membership_id = find_users(org=self._ORG, role=whom)[1]['membership_id']
user = find_users(org=self._ORG, role=who)[0]["username"]
membership_id = find_users(org=self._ORG, role=whom)[1]["membership_id"]
if is_allow:
self._test_can_change_membership(user, membership_id, new_role)

@ -3,129 +3,156 @@
#
# SPDX-License-Identifier: MIT
from copy import deepcopy
from http import HTTPStatus
import pytest
from shared.utils.config import get_method, options_method, patch_method, delete_method
from deepdiff import DeepDiff
from copy import deepcopy
from shared.utils.config import delete_method, get_method, options_method, patch_method
class TestMetadataOrganizations:
_ORG = 2
@pytest.mark.parametrize('privilege, role, is_member', [
('admin', None, None),
('user', None, False),
('business', None, False),
('worker', None, False),
(None, 'owner', True),
(None, 'maintainer', True),
(None, 'worker', True),
(None, 'supervisor', True),
])
def test_can_send_options_request(self, privilege, role, is_member,
find_users, organizations):
@pytest.mark.parametrize(
"privilege, role, is_member",
[
("admin", None, None),
("user", None, False),
("business", None, False),
("worker", None, False),
(None, "owner", True),
(None, "maintainer", True),
(None, "worker", True),
(None, "supervisor", True),
],
)
def test_can_send_options_request(self, privilege, role, is_member, find_users, organizations):
exclude_org = None if is_member else self._ORG
org = self._ORG if is_member else None
user = find_users(privilege=privilege, role=role, org=org,
exclude_org=exclude_org)[0]['username']
user = find_users(privilege=privilege, role=role, org=org, exclude_org=exclude_org)[0][
"username"
]
response = options_method(user, f'organizations')
response = options_method(user, f"organizations")
assert response.status_code == HTTPStatus.OK
response = options_method(user, f'organizations/{self._ORG}')
response = options_method(user, f"organizations/{self._ORG}")
assert response.status_code == HTTPStatus.OK
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetOrganizations:
_ORG = 2
@pytest.mark.parametrize('privilege, role, is_member, is_allow', [
('admin', None, None, True),
('user', None, False, False),
('business', None, False, False),
('worker', None, False, False),
(None, 'owner', True, True),
(None, 'maintainer', True, True),
(None, 'worker', True, True),
(None, 'supervisor', True, True),
])
def test_can_see_specific_organization(self, privilege, role, is_member,
is_allow, find_users, organizations):
@pytest.mark.parametrize(
"privilege, role, is_member, is_allow",
[
("admin", None, None, True),
("user", None, False, False),
("business", None, False, False),
("worker", None, False, False),
(None, "owner", True, True),
(None, "maintainer", True, True),
(None, "worker", True, True),
(None, "supervisor", True, True),
],
)
def test_can_see_specific_organization(
self, privilege, role, is_member, is_allow, find_users, organizations
):
exclude_org = None if is_member else self._ORG
org = self._ORG if is_member else None
user = find_users(privilege=privilege, role=role, org=org,
exclude_org=exclude_org)[0]['username']
user = find_users(privilege=privilege, role=role, org=org, exclude_org=exclude_org)[0][
"username"
]
response = get_method(user, f'organizations/{self._ORG}')
response = get_method(user, f"organizations/{self._ORG}")
if is_allow:
assert response.status_code == HTTPStatus.OK
assert DeepDiff(organizations[self._ORG], response.json()) == {}
else:
assert response.status_code == HTTPStatus.NOT_FOUND
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchOrganizations:
_ORG = 2
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def request_data(self):
return {'slug': 'new', 'name': 'new', 'description': 'new',
'contact': {'email': 'new@cvat.org'}}
@pytest.fixture(scope='class')
return {
"slug": "new",
"name": "new",
"description": "new",
"contact": {"email": "new@cvat.org"},
}
@pytest.fixture(scope="class")
def expected_data(self, organizations, request_data):
data = deepcopy(organizations[self._ORG])
data.update(request_data)
return data
@pytest.mark.parametrize('privilege, role, is_member, is_allow', [
('admin', None, None, True),
('user', None, False, False),
('business', None, False, False),
('worker', None, False, False),
(None, 'owner', True, True),
(None, 'maintainer', True, True),
(None, 'worker', True, False),
(None, 'supervisor', True, False),
])
def test_can_update_specific_organization(self, privilege, role, is_member,
is_allow, find_users, request_data, expected_data):
@pytest.mark.parametrize(
"privilege, role, is_member, is_allow",
[
("admin", None, None, True),
("user", None, False, False),
("business", None, False, False),
("worker", None, False, False),
(None, "owner", True, True),
(None, "maintainer", True, True),
(None, "worker", True, False),
(None, "supervisor", True, False),
],
)
def test_can_update_specific_organization(
self, privilege, role, is_member, is_allow, find_users, request_data, expected_data
):
exclude_org = None if is_member else self._ORG
org = self._ORG if is_member else None
user = find_users(privilege=privilege, role=role, org=org,
exclude_org=exclude_org)[0]['username']
user = find_users(privilege=privilege, role=role, org=org, exclude_org=exclude_org)[0][
"username"
]
response = patch_method(user, f'organizations/{self._ORG}', request_data)
response = patch_method(user, f"organizations/{self._ORG}", request_data)
if is_allow:
assert response.status_code == HTTPStatus.OK
assert DeepDiff(expected_data, response.json(),
exclude_paths="root['updated_date']") == {}
assert (
DeepDiff(expected_data, response.json(), exclude_paths="root['updated_date']") == {}
)
else:
assert response.status_code != HTTPStatus.OK
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestDeleteOrganizations:
_ORG = 2
@pytest.mark.parametrize('privilege, role, is_member, is_allow', [
('admin', None, None, True),
(None, 'owner', True, True),
(None, 'maintainer', True, False),
(None, 'worker', True, False),
(None, 'supervisor', True, False),
('user', None, False, False),
('business', None, False, False),
('worker', None, False, False),
])
def test_can_delete(self, privilege, role, is_member,
is_allow, find_users):
@pytest.mark.parametrize(
"privilege, role, is_member, is_allow",
[
("admin", None, None, True),
(None, "owner", True, True),
(None, "maintainer", True, False),
(None, "worker", True, False),
(None, "supervisor", True, False),
("user", None, False, False),
("business", None, False, False),
("worker", None, False, False),
],
)
def test_can_delete(self, privilege, role, is_member, is_allow, find_users):
exclude_org = None if is_member else self._ORG
org = self._ORG if is_member else None
user = find_users(privilege=privilege, role=role, org=org,
exclude_org=exclude_org)[0]['username']
user = find_users(privilege=privilege, role=role, org=org, exclude_org=exclude_org)[0][
"username"
]
response = delete_method(user, f'organizations/{self._ORG}')
response = delete_method(user, f"organizations/{self._ORG}")
if is_allow:
assert response.status_code == HTTPStatus.NO_CONTENT

@ -4,25 +4,25 @@
# SPDX-License-Identifier: MIT
import io
from copy import deepcopy
from http import HTTPStatus
from itertools import groupby, product
from time import sleep
import pytest
from copy import deepcopy
from deepdiff import DeepDiff
from shared.utils.config import get_method, patch_method, make_api_client
from shared.utils.config import get_method, make_api_client, patch_method
from .utils import export_dataset
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetProjects:
def _find_project_by_user_org(self, user, projects, is_project_staff_flag, is_project_staff):
for p in projects:
if is_project_staff(user['id'], p['id']) == is_project_staff_flag:
return p['id']
if is_project_staff(user["id"], p["id"]) == is_project_staff_flag:
return p["id"]
def _test_response_200(self, username, project_id, **kwargs):
with make_api_client(username) as api_client:
@ -32,84 +32,98 @@ class TestGetProjects:
def _test_response_403(self, username, project_id):
with make_api_client(username) as api_client:
(_, response) = api_client.projects_api.retrieve(project_id,
_parse_response=False, _check_status=False)
(_, response) = api_client.projects_api.retrieve(
project_id, _parse_response=False, _check_status=False
)
assert response.status == HTTPStatus.FORBIDDEN
# Admin can see any project even he has no ownerships for this project.
def test_project_admin_accessibility(self, projects, find_users, is_project_staff, org_staff):
users = find_users(privilege='admin')
users = find_users(privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['organization'])
and user['id'] not in org_staff(project['organization'])
if not is_project_staff(user["id"], project["organization"])
and user["id"] not in org_staff(project["organization"])
)
self._test_response_200(user['username'], project['id'])
self._test_response_200(user["username"], project["id"])
# Project owner or project assignee can see project.
def test_project_owner_accessibility(self, projects):
for p in projects:
if p['owner'] is not None:
if p["owner"] is not None:
project_with_owner = p
if p['assignee'] is not None:
if p["assignee"] is not None:
project_with_assignee = p
assert project_with_owner is not None
assert project_with_assignee is not None
self._test_response_200(project_with_owner['owner']['username'], project_with_owner['id'])
self._test_response_200(project_with_assignee['assignee']['username'], project_with_assignee['id'])
self._test_response_200(project_with_owner["owner"]["username"], project_with_owner["id"])
self._test_response_200(
project_with_assignee["assignee"]["username"], project_with_assignee["id"]
)
def test_user_cannot_see_project(self, projects, find_users, is_project_staff, org_staff):
users = find_users(exclude_privilege='admin')
users = find_users(exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['organization'])
and user['id'] not in org_staff(project['organization'])
if not is_project_staff(user["id"], project["organization"])
and user["id"] not in org_staff(project["organization"])
)
self._test_response_403(user["username"], project["id"])
@pytest.mark.parametrize("role", ("supervisor", "worker"))
def test_if_supervisor_or_worker_cannot_see_project(
self, projects, is_project_staff, find_users, role
):
user, pid = next(
(
(user, project["id"])
for user in find_users(role=role, exclude_privilege="admin")
for project in projects
if project["organization"] == user["org"]
and not is_project_staff(user["id"], project["id"])
)
)
self._test_response_403(user["username"], pid)
@pytest.mark.parametrize("role", ("maintainer", "owner"))
def test_if_maintainer_or_owner_can_see_project(
self, find_users, projects, is_project_staff, role
):
user, pid = next(
(
(user, project["id"])
for user in find_users(role=role, exclude_privilege="admin")
for project in projects
if project["organization"] == user["org"]
and not is_project_staff(user["id"], project["id"])
)
)
self._test_response_403(user['username'], project['id'])
@pytest.mark.parametrize('role', ('supervisor', 'worker'))
def test_if_supervisor_or_worker_cannot_see_project(self, projects, is_project_staff,
find_users, role):
user, pid = next((
(user, project['id'])
for user in find_users(role=role, exclude_privilege='admin')
for project in projects
if project['organization'] == user['org'] \
and not is_project_staff(user['id'], project['id'])
))
self._test_response_403(user['username'], pid)
@pytest.mark.parametrize('role', ('maintainer', 'owner'))
def test_if_maintainer_or_owner_can_see_project(self, find_users, projects, is_project_staff, role):
user, pid = next((
(user, project['id'])
for user in find_users(role=role, exclude_privilege='admin')
for project in projects
if project['organization'] == user['org'] \
and not is_project_staff(user['id'], project['id'])
))
self._test_response_200(user['username'], pid, org_id=user['org'])
@pytest.mark.parametrize('role', ('supervisor', 'worker'))
def test_if_org_member_supervisor_or_worker_can_see_project(self, projects,
find_users, is_project_staff, role):
user, pid = next((
(user, project['id'])
for user in find_users(role=role, exclude_privilege='admin')
for project in projects
if project['organization'] == user['org'] \
and is_project_staff(user['id'], project['id'])
))
self._test_response_200(user['username'], pid, org_id=user['org'])
self._test_response_200(user["username"], pid, org_id=user["org"])
@pytest.mark.parametrize("role", ("supervisor", "worker"))
def test_if_org_member_supervisor_or_worker_can_see_project(
self, projects, find_users, is_project_staff, role
):
user, pid = next(
(
(user, project["id"])
for user in find_users(role=role, exclude_privilege="admin")
for project in projects
if project["organization"] == user["org"]
and is_project_staff(user["id"], project["id"])
)
)
self._test_response_200(user["username"], pid, org_id=user["org"])
class TestGetProjectBackup:
def _test_can_get_project_backup(self, username, pid, **kwargs):
@ -128,105 +142,130 @@ class TestGetProjectBackup:
def test_admin_can_get_project_backup(self, projects):
project = list(projects)[0]
self._test_can_get_project_backup('admin1', project['id'])
self._test_can_get_project_backup("admin1", project["id"])
# User that not in [project:owner, project:assignee] cannot get project backup.
def test_user_cannot_get_project_backup(self, find_users, projects, is_project_staff):
users = find_users(exclude_privilege='admin')
users = find_users(exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
if not is_project_staff(user["id"], project["id"])
)
self._test_cannot_get_project_backup(user['username'], project['id'])
self._test_cannot_get_project_backup(user["username"], project["id"])
# Org worker that not in [project:owner, project:assignee] cannot get project backup.
def test_org_worker_cannot_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='worker', exclude_privilege='admin')
def test_org_worker_cannot_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="worker", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_cannot_get_project_backup(user['username'], project['id'], org_id=project['organization'])
self._test_cannot_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
# Org worker that in [project:owner, project:assignee] can get project backup.
def test_org_worker_can_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='worker', exclude_privilege='admin')
def test_org_worker_can_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="worker", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_can_get_project_backup(user['username'], project['id'], org_id=project['organization'])
self._test_can_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
# Org supervisor that in [project:owner, project:assignee] can get project backup.
def test_org_supervisor_can_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='supervisor', exclude_privilege='admin')
def test_org_supervisor_can_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="supervisor", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_can_get_project_backup(user['username'], project['id'], org_id=project['organization'])
self._test_can_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
# Org supervisor that not in [project:owner, project:assignee] cannot get project backup.
def test_org_supervisor_cannot_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='supervisor', exclude_privilege='admin')
def test_org_supervisor_cannot_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="supervisor", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_cannot_get_project_backup(user['username'], project['id'], org_id=project['organization'])
self._test_cannot_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
# Org maintainer that not in [project:owner, project:assignee] can get project backup.
def test_org_maintainer_can_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='maintainer', exclude_privilege='admin')
def test_org_maintainer_can_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="maintainer", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_can_get_project_backup(user['username'], project['id'], org_id=project['organization'])
self._test_can_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
# Org owner that not in [project:owner, project:assignee] can get project backup.
def test_org_owner_can_get_project_backup(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='owner', exclude_privilege='admin')
def test_org_owner_can_get_project_backup(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="owner", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
self._test_can_get_project_backup(
user["username"], project["id"], org_id=project["organization"]
)
self._test_can_get_project_backup(user['username'], project['id'], org_id=project['organization'])
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPostProjects:
def _test_create_project_201(self, user, spec, **kwargs):
with make_api_client(user) as api_client:
@ -235,53 +274,48 @@ class TestPostProjects:
def _test_create_project_403(self, user, spec, **kwargs):
with make_api_client(user) as api_client:
(_, response) = api_client.projects_api.create(spec, **kwargs,
_parse_response=False, _check_status=False)
(_, response) = api_client.projects_api.create(
spec, **kwargs, _parse_response=False, _check_status=False
)
assert response.status == HTTPStatus.FORBIDDEN
def test_if_worker_cannot_create_project(self, find_users):
workers = find_users(privilege='worker')
workers = find_users(privilege="worker")
assert len(workers)
username = workers[0]['username']
spec = {
'name': f'test {username} tries to create a project'
}
username = workers[0]["username"]
spec = {"name": f"test {username} tries to create a project"}
self._test_create_project_403(username, spec)
@pytest.mark.parametrize('privilege', ('admin', 'business', 'user'))
@pytest.mark.parametrize("privilege", ("admin", "business", "user"))
def test_if_user_can_create_project(self, find_users, privilege):
privileged_users = find_users(privilege=privilege)
assert len(privileged_users)
username = privileged_users[0]['username']
spec = {
'name': f'test {username} tries to create a project'
}
username = privileged_users[0]["username"]
spec = {"name": f"test {username} tries to create a project"}
self._test_create_project_201(username, spec)
def test_if_user_cannot_have_more_than_3_projects(self, projects, find_users):
users = find_users(privilege='user')
users = find_users(privilege="user")
user_id, user_projects = next(
(user_id, len(list(projects)))
for user_id, projects in groupby(projects, lambda a: a['owner']['id'])
for user_id, projects in groupby(projects, lambda a: a["owner"]["id"])
if len(list(projects)) < 3
)
user = users[user_id]
for i in range(1, 4 - user_projects):
spec = {
'name': f'test: {user["username"]} tries to create a project number {user_projects + i}'
"name": f'test: {user["username"]} tries to create a project number {user_projects + i}'
}
self._test_create_project_201(user['username'], spec)
self._test_create_project_201(user["username"], spec)
spec = {
'name': f'test {user["username"]} tries to create more than 3 projects'
}
self._test_create_project_403(user['username'], spec)
spec = {"name": f'test {user["username"]} tries to create more than 3 projects'}
self._test_create_project_403(user["username"], spec)
@pytest.mark.parametrize('privilege', ('admin', 'business'))
@pytest.mark.parametrize("privilege", ("admin", "business"))
def test_if_user_can_have_more_than_3_projects(self, find_users, privilege):
privileged_users = find_users(privilege=privilege)
assert len(privileged_users)
@ -290,105 +324,111 @@ class TestPostProjects:
for i in range(1, 5):
spec = {
'name': f'test: {user["username"]} with privilege {privilege} tries to create a project number {i}'
"name": f'test: {user["username"]} with privilege {privilege} tries to create a project number {i}'
}
self._test_create_project_201(user['username'], spec)
self._test_create_project_201(user["username"], spec)
def test_if_org_worker_cannot_crate_project(self, find_users):
workers = find_users(role='worker')
workers = find_users(role="worker")
worker = next(u for u in workers if u['org'])
worker = next(u for u in workers if u["org"])
spec = {
'name': f'test: worker {worker["username"]} creating a project for his organization',
"name": f'test: worker {worker["username"]} creating a project for his organization',
}
self._test_create_project_403(worker['username'], spec, org_id=worker['org'])
self._test_create_project_403(worker["username"], spec, org_id=worker["org"])
@pytest.mark.parametrize('role', ('supervisor', 'maintainer', 'owner'))
@pytest.mark.parametrize("role", ("supervisor", "maintainer", "owner"))
def test_if_org_role_can_create_project(self, find_users, role):
privileged_users = find_users(role=role)
assert len(privileged_users)
user = next(u for u in privileged_users if u['org'])
user = next(u for u in privileged_users if u["org"])
spec = {
'name': f'test: worker {user["username"]} creating a project for his organization',
"name": f'test: worker {user["username"]} creating a project for his organization',
}
self._test_create_project_201(user['username'], spec, org_id=user['org'])
self._test_create_project_201(user["username"], spec, org_id=user["org"])
@pytest.mark.usefixtures("changedb")
class TestImportExportDatasetProject:
def _test_export_project(self, username, pid, format_name):
with make_api_client(username) as api_client:
return export_dataset(api_client.projects_api.retrieve_dataset_endpoint,
id=pid, format=format_name)
return export_dataset(
api_client.projects_api.retrieve_dataset_endpoint, id=pid, format=format_name
)
def _test_import_project(self, username, project_id, format_name, data):
with make_api_client(username) as api_client:
(_, response) = api_client.projects_api.create_dataset(id=project_id,
format=format_name, dataset_write_request=deepcopy(data),
_content_type="multipart/form-data")
(_, response) = api_client.projects_api.create_dataset(
id=project_id,
format=format_name,
dataset_write_request=deepcopy(data),
_content_type="multipart/form-data",
)
assert response.status == HTTPStatus.ACCEPTED
while True:
# TODO: It's better be refactored to a separate endpoint to get request status
(_, response) = api_client.projects_api.retrieve_dataset(project_id,
action='import_status')
(_, response) = api_client.projects_api.retrieve_dataset(
project_id, action="import_status"
)
if response.status == HTTPStatus.CREATED:
break
def test_can_import_dataset_in_org(self, admin_user):
project_id = 4
response = self._test_export_project(admin_user, project_id, 'CVAT for images 1.1')
response = self._test_export_project(admin_user, project_id, "CVAT for images 1.1")
tmp_file = io.BytesIO(response.data)
tmp_file.name = 'dataset.zip'
tmp_file.name = "dataset.zip"
import_data = {
'dataset_file': tmp_file,
"dataset_file": tmp_file,
}
self._test_import_project(admin_user, project_id, 'CVAT 1.1', import_data)
self._test_import_project(admin_user, project_id, "CVAT 1.1", import_data)
def test_can_export_and_import_dataset_with_skeletons_coco_keypoints(self, admin_user):
project_id = 5
response = self._test_export_project(admin_user, project_id, 'COCO Keypoints 1.0')
response = self._test_export_project(admin_user, project_id, "COCO Keypoints 1.0")
tmp_file = io.BytesIO(response.data)
tmp_file.name = 'dataset.zip'
tmp_file.name = "dataset.zip"
import_data = {
'dataset_file': tmp_file,
"dataset_file": tmp_file,
}
self._test_import_project(admin_user, project_id, 'COCO Keypoints 1.0', import_data)
self._test_import_project(admin_user, project_id, "COCO Keypoints 1.0", import_data)
def test_can_export_and_import_dataset_with_skeletons_cvat_for_images(self, admin_user):
project_id = 5
response = self._test_export_project(admin_user, project_id, 'CVAT for images 1.1')
response = self._test_export_project(admin_user, project_id, "CVAT for images 1.1")
tmp_file = io.BytesIO(response.data)
tmp_file.name = 'dataset.zip'
tmp_file.name = "dataset.zip"
import_data = {
'dataset_file': tmp_file,
"dataset_file": tmp_file,
}
self._test_import_project(admin_user, project_id, 'CVAT 1.1', import_data)
self._test_import_project(admin_user, project_id, "CVAT 1.1", import_data)
def test_can_export_and_import_dataset_with_skeletons_cvat_for_video(self, admin_user):
project_id = 5
response = self._test_export_project(admin_user, project_id, 'CVAT for video 1.1')
response = self._test_export_project(admin_user, project_id, "CVAT for video 1.1")
tmp_file = io.BytesIO(response.data)
tmp_file.name = 'dataset.zip'
tmp_file.name = "dataset.zip"
import_data = {
'dataset_file': tmp_file,
"dataset_file": tmp_file,
}
self._test_import_project(admin_user, project_id, 'CVAT 1.1', import_data)
self._test_import_project(admin_user, project_id, "CVAT 1.1", import_data)
def _test_can_get_project_backup(self, username, pid, **kwargs):
for _ in range(30):
@ -406,130 +446,162 @@ class TestImportExportDatasetProject:
response = self._test_can_get_project_backup(admin_user, project_id)
tmp_file = io.BytesIO(response.content)
tmp_file.name = 'dataset.zip'
tmp_file.name = "dataset.zip"
import_data = {
'project_file': tmp_file,
"project_file": tmp_file,
}
with make_api_client(admin_user) as api_client:
(_, response) = api_client.projects_api.create_backup(
backup_write_request=deepcopy(import_data),
_content_type="multipart/form-data")
backup_write_request=deepcopy(import_data), _content_type="multipart/form-data"
)
assert response.status == HTTPStatus.ACCEPTED
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchProjectLabel:
def test_admin_can_delete_label(self, projects):
project = deepcopy(list(projects)[1])
labels = project['labels'][0]
labels.update({'deleted': True})
response = patch_method('admin1', f'/projects/{project["id"]}', {'labels': [labels]})
labels = project["labels"][0]
labels.update({"deleted": True})
response = patch_method("admin1", f'/projects/{project["id"]}', {"labels": [labels]})
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) - 1
assert len(response.json()["labels"]) == len(project["labels"]) - 1
def test_admin_can_delete_skeleton_label(self, projects):
project = deepcopy(projects[5])
labels = project['labels'][0]
labels.update({'deleted': True})
response = patch_method('admin1', f'/projects/{project["id"]}', {'labels': [labels]})
labels = project["labels"][0]
labels.update({"deleted": True})
response = patch_method("admin1", f'/projects/{project["id"]}', {"labels": [labels]})
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) - 4
assert len(response.json()["labels"]) == len(project["labels"]) - 4
def test_admin_can_rename_label(self, projects):
project = deepcopy(list(projects)[0])
labels = project['labels'][0]
labels.update({'name': 'new name'})
response = patch_method('admin1', f'/projects/{project["id"]}', {'labels': [labels]})
labels = project["labels"][0]
labels.update({"name": "new name"})
response = patch_method("admin1", f'/projects/{project["id"]}', {"labels": [labels]})
assert response.status_code == HTTPStatus.OK
assert DeepDiff(response.json()['labels'], project['labels'], ignore_order=True) == {}
assert DeepDiff(response.json()["labels"], project["labels"], ignore_order=True) == {}
def test_admin_can_add_label(self, projects):
project = list(projects)[0]
labels = {'name': 'new name'}
response = patch_method('admin1', f'/projects/{project["id"]}', {'labels': [labels]})
labels = {"name": "new name"}
response = patch_method("admin1", f'/projects/{project["id"]}', {"labels": [labels]})
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) + 1
assert len(response.json()["labels"]) == len(project["labels"]) + 1
# Org maintainer can add label even he is not in [project:owner, project:assignee]
def test_org_maintainer_can_add_label(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='maintainer', exclude_privilege='admin')
def test_org_maintainer_can_add_label(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="maintainer", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
labels = {'name': 'new name'}
response = patch_method(user['username'], f'/projects/{project["id"]}', {'labels': [labels]}, org_id=project['organization'])
labels = {"name": "new name"}
response = patch_method(
user["username"],
f'/projects/{project["id"]}',
{"labels": [labels]},
org_id=project["organization"],
)
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) + 1
assert len(response.json()["labels"]) == len(project["labels"]) + 1
# Org supervisor cannot add label
def test_org_supervisor_can_add_label(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='supervisor', exclude_privilege='admin')
def test_org_supervisor_can_add_label(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="supervisor", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
labels = {'name': 'new name'}
response = patch_method(user['username'], f'/projects/{project["id"]}', {'labels': [labels]}, org_id=project['organization'])
labels = {"name": "new name"}
response = patch_method(
user["username"],
f'/projects/{project["id"]}',
{"labels": [labels]},
org_id=project["organization"],
)
assert response.status_code == HTTPStatus.FORBIDDEN
# Org worker cannot add label
def test_org_worker_cannot_add_label(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='worker', exclude_privilege='admin')
def test_org_worker_cannot_add_label(
self, find_users, projects, is_project_staff, is_org_member
):
users = find_users(role="worker", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
labels = {'name': 'new name'}
response = patch_method(user['username'], f'/projects/{project["id"]}', {'labels': [labels]}, org_id=project['organization'])
labels = {"name": "new name"}
response = patch_method(
user["username"],
f'/projects/{project["id"]}',
{"labels": [labels]},
org_id=project["organization"],
)
assert response.status_code == HTTPStatus.FORBIDDEN
# Org worker that in [project:owner, project:assignee] can add label
def test_org_worker_can_add_label(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='worker', exclude_privilege='admin')
users = find_users(role="worker", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
labels = {'name': 'new name'}
response = patch_method(user['username'], f'/projects/{project["id"]}', {'labels': [labels]}, org_id=project['organization'])
labels = {"name": "new name"}
response = patch_method(
user["username"],
f'/projects/{project["id"]}',
{"labels": [labels]},
org_id=project["organization"],
)
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) + 1
assert len(response.json()["labels"]) == len(project["labels"]) + 1
# Org owner can add label even he is not in [project:owner, project:assignee]
def test_org_owner_can_add_label(self, find_users, projects, is_project_staff, is_org_member):
users = find_users(role='owner', exclude_privilege='admin')
users = find_users(role="owner", exclude_privilege="admin")
user, project = next(
(user, project)
for user, project in product(users, projects)
if not is_project_staff(user['id'], project['id'])
and project['organization']
and is_org_member(user['id'], project['organization'])
if not is_project_staff(user["id"], project["id"])
and project["organization"]
and is_org_member(user["id"], project["organization"])
)
labels = {'name': 'new name'}
response = patch_method(user['username'], f'/projects/{project["id"]}', {'labels': [labels]}, org_id=project['organization'])
labels = {"name": "new name"}
response = patch_method(
user["username"],
f'/projects/{project["id"]}',
{"labels": [labels]},
org_id=project["organization"],
)
assert response.status_code == HTTPStatus.OK
assert len(response.json()['labels']) == len(project['labels']) + 1
assert len(response.json()["labels"]) == len(project["labels"]) + 1

@ -13,34 +13,36 @@ from shared.utils.config import get_method, post_method
def _post_task_remote_data(username, task_id, resources):
data = {
'remote_files': resources,
'image_quality': 30,
"remote_files": resources,
"image_quality": 30,
}
return post_method(username, f'tasks/{task_id}/data', data)
return post_method(username, f"tasks/{task_id}/data", data)
def _wait_until_task_is_created(username, task_id):
url = f'tasks/{task_id}/status'
url = f"tasks/{task_id}/status"
for _ in range(100):
response = get_method(username, url)
response_json = response.json()
if response_json['state'] == 'Finished' or response_json['state'] == 'Failed':
if response_json["state"] == "Finished" or response_json["state"] == "Failed":
return response
sleep(1)
raise Exception('Cannot create task')
raise Exception("Cannot create task")
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestCreateFromRemote:
task_id = 12
def _test_can_create(self, user, task_id, resources):
response = _post_task_remote_data(user, task_id, resources)
assert response.status_code == HTTPStatus.ACCEPTED
response = _wait_until_task_is_created(user, task_id)
response_json = response.json()
assert response_json['state'] == 'Finished'
assert response_json["state"] == "Finished"
def _test_cannot_create(self, user, task_id, resources):
response = _post_task_remote_data(user, task_id, resources)
@ -48,16 +50,16 @@ class TestCreateFromRemote:
response = _wait_until_task_is_created(user, task_id)
response_json = response.json()
assert response_json['state'] == 'Failed'
assert response_json["state"] == "Failed"
def test_cannot_create(self, find_users):
user = find_users(privilege='admin')[0]['username']
remote_resources = ['http://localhost/favicon.ico']
user = find_users(privilege="admin")[0]["username"]
remote_resources = ["http://localhost/favicon.ico"]
self._test_cannot_create(user, self.task_id, remote_resources)
def test_can_create(self, find_users):
user = find_users(privilege='admin')[0]['username']
remote_resources = ['https://opencv.github.io/cvat/favicons/favicon-32x32.png']
user = find_users(privilege="admin")[0]["username"]
remote_resources = ["https://opencv.github.io/cvat/favicons/favicon-32x32.png"]
self._test_can_create(user, self.task_id, remote_resources)

@ -3,64 +3,74 @@
#
# SPDX-License-Identifier: MIT
import pytest
import boto3
import functools
import json
from http import HTTPStatus
import boto3
import pytest
from botocore.exceptions import ClientError
from http import HTTPStatus
from shared.utils.config import (
get_method, post_method, MINIO_KEY, MINIO_SECRET_KEY, MINIO_ENDPOINT_URL,
MINIO_ENDPOINT_URL,
MINIO_KEY,
MINIO_SECRET_KEY,
get_method,
post_method,
)
FILENAME_TEMPLATE = 'cvat/{}/{}.zip'
FORMAT = 'COCO 1.0'
FILENAME_TEMPLATE = "cvat/{}/{}.zip"
FORMAT = "COCO 1.0"
def _use_custom_settings(obj, resource, cloud_storage_id):
return {
'filename': FILENAME_TEMPLATE.format(obj, resource),
'use_default_location': False,
'location': 'cloud_storage',
'cloud_storage_id': cloud_storage_id,
'format': FORMAT,
"filename": FILENAME_TEMPLATE.format(obj, resource),
"use_default_location": False,
"location": "cloud_storage",
"cloud_storage_id": cloud_storage_id,
"format": FORMAT,
}
def _use_default_settings(obj, resource):
return {
'filename': FILENAME_TEMPLATE.format(obj, resource),
'use_default_location': True,
'format': FORMAT,
"filename": FILENAME_TEMPLATE.format(obj, resource),
"use_default_location": True,
"format": FORMAT,
}
def define_client():
s3 = boto3.resource(
's3',
"s3",
aws_access_key_id=MINIO_KEY,
aws_secret_access_key=MINIO_SECRET_KEY,
endpoint_url= MINIO_ENDPOINT_URL,
endpoint_url=MINIO_ENDPOINT_URL,
)
return s3.meta.client
def assert_file_does_not_exist(client, bucket, filename):
try:
client.head_object(Bucket=bucket, Key=filename)
raise AssertionError(f'File {filename} on bucket {bucket} already exists')
raise AssertionError(f"File {filename} on bucket {bucket} already exists")
except ClientError:
pass
def assert_file_exists(client, bucket, filename):
try:
client.head_object(Bucket=bucket, Key=filename)
except ClientError:
raise AssertionError(f"File {filename} on bucket {bucket} doesn't exist")
def assert_file_status(func):
@functools.wraps(func)
def wrapper(user, storage_conf, *args, **kwargs):
filename = kwargs['filename']
bucket = storage_conf['resource']
filename = kwargs["filename"]
bucket = storage_conf["resource"]
# get storage client
client = define_client()
# check that file doesn't exist on the bucket
@ -68,88 +78,109 @@ def assert_file_status(func):
func(user, storage_conf, *args, **kwargs)
# check that file exists on the bucket
assert_file_exists(client, bucket, filename)
return wrapper
def remove_asset(bucket, filename):
client = define_client()
client.delete_object(Bucket=bucket, Key=filename)
@assert_file_status
def _save_resource_to_cloud_storage(user, storage_conf, obj_id, obj, resource, **kwargs):
response = get_method(user, f'{obj}/{obj_id}/{resource}', **kwargs)
response = get_method(user, f"{obj}/{obj_id}/{resource}", **kwargs)
status = response.status_code
while status != HTTPStatus.OK:
assert status in (HTTPStatus.CREATED, HTTPStatus.ACCEPTED)
response = get_method(user, f'{obj}/{obj_id}/{resource}', action='download', **kwargs)
response = get_method(user, f"{obj}/{obj_id}/{resource}", action="download", **kwargs)
status = response.status_code
def _idempotent_saving_resource_to_cloud_storage(*args, **kwargs):
_save_resource_to_cloud_storage(*args, **kwargs)
remove_asset(args[1]['resource'], kwargs['filename'])
remove_asset(args[1]["resource"], kwargs["filename"])
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestSaveResource:
_USERNAME = 'admin1'
_USERNAME = "admin1"
_ORG = 2
@pytest.mark.parametrize('cloud_storage_id', [3])
@pytest.mark.parametrize('obj_id, obj, resource', [
(2, 'projects', 'annotations'),
(2, 'projects', 'dataset'),
(2, 'projects', 'backup'),
(11, 'tasks', 'annotations'),
(11, 'tasks', 'dataset'),
(11, 'tasks', 'backup'),
(16, 'jobs', 'annotations'),
(16, 'jobs', 'dataset'),
])
@pytest.mark.parametrize("cloud_storage_id", [3])
@pytest.mark.parametrize(
"obj_id, obj, resource",
[
(2, "projects", "annotations"),
(2, "projects", "dataset"),
(2, "projects", "backup"),
(11, "tasks", "annotations"),
(11, "tasks", "dataset"),
(11, "tasks", "backup"),
(16, "jobs", "annotations"),
(16, "jobs", "dataset"),
],
)
def test_save_resource_to_cloud_storage_with_specific_location(
self, cloud_storage_id, obj_id, obj, resource, cloud_storages
):
cloud_storage = cloud_storages[cloud_storage_id]
kwargs = _use_custom_settings(obj, resource, cloud_storage_id)
if resource == 'backup':
kwargs.pop('format')
_idempotent_saving_resource_to_cloud_storage(self._USERNAME, cloud_storage,
obj_id, obj, resource, org_id=self._ORG, **kwargs)
@pytest.mark.parametrize('obj_id, obj, resource', [
(2, 'projects', 'annotations'),
(2, 'projects', 'dataset'),
(2, 'projects', 'backup'),
(11, 'tasks', 'annotations'),
(11, 'tasks', 'dataset'),
(11, 'tasks', 'backup'),
(16, 'jobs', 'annotations'),
(16, 'jobs', 'dataset'),
])
if resource == "backup":
kwargs.pop("format")
_idempotent_saving_resource_to_cloud_storage(
self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **kwargs
)
@pytest.mark.parametrize(
"obj_id, obj, resource",
[
(2, "projects", "annotations"),
(2, "projects", "dataset"),
(2, "projects", "backup"),
(11, "tasks", "annotations"),
(11, "tasks", "dataset"),
(11, "tasks", "backup"),
(16, "jobs", "annotations"),
(16, "jobs", "dataset"),
],
)
def test_save_resource_to_cloud_storage_with_default_location(
self, obj_id, obj, resource, projects, tasks, jobs, cloud_storages,
self,
obj_id,
obj,
resource,
projects,
tasks,
jobs,
cloud_storages,
):
objects = {
'projects': projects,
'tasks': tasks,
'jobs': jobs,
"projects": projects,
"tasks": tasks,
"jobs": jobs,
}
if obj in ('projects', 'tasks'):
cloud_storage_id = objects[obj][obj_id]['target_storage']['cloud_storage_id']
if obj in ("projects", "tasks"):
cloud_storage_id = objects[obj][obj_id]["target_storage"]["cloud_storage_id"]
else:
task_id = jobs[obj_id]['task_id']
cloud_storage_id = tasks[task_id]['target_storage']['cloud_storage_id']
task_id = jobs[obj_id]["task_id"]
cloud_storage_id = tasks[task_id]["target_storage"]["cloud_storage_id"]
cloud_storage = cloud_storages[cloud_storage_id]
kwargs = _use_default_settings(obj, resource)
if resource == 'backup':
kwargs.pop('format')
if resource == "backup":
kwargs.pop("format")
_idempotent_saving_resource_to_cloud_storage(
self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **kwargs
)
_idempotent_saving_resource_to_cloud_storage(self._USERNAME, cloud_storage,
obj_id, obj, resource, org_id=self._ORG, **kwargs)
def _import_annotations_from_cloud_storage(user, obj_id, obj, **kwargs):
url = f'{obj}/{obj_id}/annotations'
url = f"{obj}/{obj_id}/annotations"
response = post_method(user, url, data=None, **kwargs)
status = response.status_code
@ -158,41 +189,47 @@ def _import_annotations_from_cloud_storage(user, obj_id, obj, **kwargs):
response = post_method(user, url, data=None, **kwargs)
status = response.status_code
def _import_backup_from_cloud_storage(user, obj_id, obj, **kwargs):
url = f'{obj}/backup'
url = f"{obj}/backup"
response = post_method(user, url, data=None, **kwargs)
status = response.status_code
while status != HTTPStatus.CREATED:
assert status == HTTPStatus.ACCEPTED
data = json.loads(response.content.decode('utf8'))
data = json.loads(response.content.decode("utf8"))
response = post_method(user, url, data=data, **kwargs)
status = response.status_code
def _import_dataset_from_cloud_storage(user, obj_id, obj, **kwargs):
url = f'{obj}/{obj_id}/dataset'
url = f"{obj}/{obj_id}/dataset"
response = post_method(user, url, data=None, **kwargs)
status = response.status_code
while status != HTTPStatus.CREATED:
assert status == HTTPStatus.ACCEPTED
response = get_method(user, url, action='import_status')
response = get_method(user, url, action="import_status")
status = response.status_code
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures('restore_cvat_data')
@pytest.mark.usefixtures("changedb")
@pytest.mark.usefixtures("restore_cvat_data")
class TestImportResource:
_USERNAME = 'admin1'
_USERNAME = "admin1"
_ORG = 2
@pytest.mark.parametrize('cloud_storage_id', [3])
@pytest.mark.parametrize('obj_id, obj, resource', [
(2, 'projects', 'dataset'),
(2, 'projects', 'backup'),
(11, 'tasks', 'annotations'),
(11, 'tasks', 'backup'),
(16, 'jobs', 'annotations'),
])
@pytest.mark.parametrize("cloud_storage_id", [3])
@pytest.mark.parametrize(
"obj_id, obj, resource",
[
(2, "projects", "dataset"),
(2, "projects", "backup"),
(11, "tasks", "annotations"),
(11, "tasks", "backup"),
(16, "jobs", "annotations"),
],
)
def test_import_resource_from_cloud_storage_with_specific_location(
self, cloud_storage_id, obj_id, obj, resource, cloud_storages
):
@ -200,50 +237,64 @@ class TestImportResource:
kwargs = _use_custom_settings(obj, resource, cloud_storage_id)
export_kwargs = _use_custom_settings(obj, resource, cloud_storage_id)
if resource == 'backup':
kwargs.pop('format')
kwargs.pop('use_default_location')
export_kwargs.pop('format')
if resource == "backup":
kwargs.pop("format")
kwargs.pop("use_default_location")
export_kwargs.pop("format")
# export current resource to cloud storage
_save_resource_to_cloud_storage(self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **export_kwargs)
_save_resource_to_cloud_storage(
self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **export_kwargs
)
import_resource = {
'annotations': _import_annotations_from_cloud_storage,
'dataset': _import_dataset_from_cloud_storage,
'backup': _import_backup_from_cloud_storage,
"annotations": _import_annotations_from_cloud_storage,
"dataset": _import_dataset_from_cloud_storage,
"backup": _import_backup_from_cloud_storage,
}
import_resource[resource](self._USERNAME, obj_id, obj, org_id=self._ORG, **kwargs)
remove_asset(cloud_storage['resource'], kwargs['filename'])
@pytest.mark.parametrize('obj_id, obj, resource', [
(2, 'projects', 'dataset'),
(11, 'tasks', 'annotations'),
(16, 'jobs', 'annotations'),
])
remove_asset(cloud_storage["resource"], kwargs["filename"])
@pytest.mark.parametrize(
"obj_id, obj, resource",
[
(2, "projects", "dataset"),
(11, "tasks", "annotations"),
(16, "jobs", "annotations"),
],
)
def test_import_resource_from_cloud_storage_with_default_location(
self, obj_id, obj, resource, projects, tasks, jobs, cloud_storages,
self,
obj_id,
obj,
resource,
projects,
tasks,
jobs,
cloud_storages,
):
objects = {
'projects': projects,
'tasks': tasks,
'jobs': jobs,
"projects": projects,
"tasks": tasks,
"jobs": jobs,
}
if obj in ('projects', 'tasks'):
cloud_storage_id = objects[obj][obj_id]['source_storage']['cloud_storage_id']
if obj in ("projects", "tasks"):
cloud_storage_id = objects[obj][obj_id]["source_storage"]["cloud_storage_id"]
else:
task_id = jobs[obj_id]['task_id']
cloud_storage_id = tasks[task_id]['source_storage']['cloud_storage_id']
task_id = jobs[obj_id]["task_id"]
cloud_storage_id = tasks[task_id]["source_storage"]["cloud_storage_id"]
cloud_storage = cloud_storages[cloud_storage_id]
kwargs = _use_default_settings(obj, resource)
# export current resource to cloud storage
_save_resource_to_cloud_storage(self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **kwargs)
_save_resource_to_cloud_storage(
self._USERNAME, cloud_storage, obj_id, obj, resource, org_id=self._ORG, **kwargs
)
import_resource = {
'annotations': _import_annotations_from_cloud_storage,
'dataset': _import_dataset_from_cloud_storage,
'backup': _import_backup_from_cloud_storage,
"annotations": _import_annotations_from_cloud_storage,
"dataset": _import_dataset_from_cloud_storage,
"backup": _import_backup_from_cloud_storage,
}
import_resource[resource](self._USERNAME, obj_id, obj, org_id=self._ORG, **kwargs)
remove_asset(cloud_storage['resource'], kwargs['filename'])
remove_asset(cloud_storage["resource"], kwargs["filename"])

@ -4,11 +4,13 @@
from http import HTTPStatus
import pytest
from shared.utils.config import make_api_client
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetServer:
def test_can_retrieve_about_unauthorized(self):
with make_api_client(user=None, password=None) as api_client:
@ -26,7 +28,7 @@ class TestGetServer:
assert len(data.exporters) != 0
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetSchema:
def test_can_get_schema_unauthorized(self):
with make_api_client(user=None, password=None) as api_client:

@ -7,107 +7,142 @@ import json
from copy import deepcopy
from http import HTTPStatus
from time import sleep
from cvat_sdk.api_client import models, apis
from cvat_sdk.core.helpers import get_paginated_collection
import pytest
from cvat_sdk.api_client import apis, models
from cvat_sdk.core.helpers import get_paginated_collection
from deepdiff import DeepDiff
from shared.utils.config import get_method, make_api_client, patch_method
from shared.utils.helpers import generate_image_files
from .utils import export_dataset
def get_cloud_storage_content(username, cloud_storage_id, manifest):
with make_api_client(username) as api_client:
(data, _) = api_client.cloudstorages_api.retrieve_content(cloud_storage_id,
manifest_path=manifest)
(data, _) = api_client.cloudstorages_api.retrieve_content(
cloud_storage_id, manifest_path=manifest
)
return data
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetTasks:
def _test_task_list_200(self, user, project_id, data, exclude_paths = '', **kwargs):
def _test_task_list_200(self, user, project_id, data, exclude_paths="", **kwargs):
with make_api_client(user) as api_client:
results = get_paginated_collection(api_client.projects_api.list_tasks_endpoint,
return_json=True, id=project_id, **kwargs)
results = get_paginated_collection(
api_client.projects_api.list_tasks_endpoint,
return_json=True,
id=project_id,
**kwargs,
)
assert DeepDiff(data, results, ignore_order=True, exclude_paths=exclude_paths) == {}
def _test_task_list_403(self, user, project_id, **kwargs):
with make_api_client(user) as api_client:
(_, response) = api_client.projects_api.list_tasks(project_id, **kwargs,
_parse_response=False, _check_status=False)
(_, response) = api_client.projects_api.list_tasks(
project_id, **kwargs, _parse_response=False, _check_status=False
)
assert response.status == HTTPStatus.FORBIDDEN
def _test_users_to_see_task_list(self, project_id, tasks, users, is_staff, is_allow, is_project_staff, **kwargs):
def _test_users_to_see_task_list(
self, project_id, tasks, users, is_staff, is_allow, is_project_staff, **kwargs
):
if is_staff:
users = [user for user in users if is_project_staff(user['id'], project_id) ]
users = [user for user in users if is_project_staff(user["id"], project_id)]
else:
users = [user for user in users if not is_project_staff(user['id'], project_id)]
users = [user for user in users if not is_project_staff(user["id"], project_id)]
assert len(users)
for user in users:
if is_allow:
self._test_task_list_200(user['username'], project_id, tasks, **kwargs)
self._test_task_list_200(user["username"], project_id, tasks, **kwargs)
else:
self._test_task_list_403(user['username'], project_id, **kwargs)
self._test_task_list_403(user["username"], project_id, **kwargs)
def _test_assigned_users_to_see_task_data(self, tasks, users, is_task_staff, **kwargs):
for task in tasks:
staff_users = [user for user in users if is_task_staff(user['id'], task['id'])]
staff_users = [user for user in users if is_task_staff(user["id"], task["id"])]
assert len(staff_users)
for user in staff_users:
with make_api_client(user['username']) as api_client:
with make_api_client(user["username"]) as api_client:
(_, response) = api_client.tasks_api.list(**kwargs)
assert response.status == HTTPStatus.OK
response_data = json.loads(response.data)
assert any(_task['id'] == task['id'] for _task in response_data['results'])
@pytest.mark.parametrize('project_id', [1])
@pytest.mark.parametrize('groups, is_staff, is_allow', [
('admin', False, True),
('business', False, False),
])
def test_project_tasks_visibility(self, project_id, groups, users, tasks, is_staff, is_allow, find_users, is_project_staff):
assert any(_task["id"] == task["id"] for _task in response_data["results"])
@pytest.mark.parametrize("project_id", [1])
@pytest.mark.parametrize(
"groups, is_staff, is_allow",
[
("admin", False, True),
("business", False, False),
],
)
def test_project_tasks_visibility(
self, project_id, groups, users, tasks, is_staff, is_allow, find_users, is_project_staff
):
users = find_users(privilege=groups)
tasks = list(filter(lambda x: x['project_id'] == project_id, tasks))
tasks = list(filter(lambda x: x["project_id"] == project_id, tasks))
assert len(tasks)
self._test_users_to_see_task_list(project_id, tasks, users, is_staff, is_allow, is_project_staff)
self._test_users_to_see_task_list(
project_id, tasks, users, is_staff, is_allow, is_project_staff
)
@pytest.mark.parametrize('project_id, groups', [(1, 'user')])
def test_task_assigned_to_see_task(self, project_id, groups, users, tasks, find_users, is_task_staff):
@pytest.mark.parametrize("project_id, groups", [(1, "user")])
def test_task_assigned_to_see_task(
self, project_id, groups, users, tasks, find_users, is_task_staff
):
users = find_users(privilege=groups)
tasks = list(filter(lambda x: x['project_id'] == project_id and x['assignee'], tasks))
tasks = list(filter(lambda x: x["project_id"] == project_id and x["assignee"], tasks))
assert len(tasks)
self._test_assigned_users_to_see_task_data(tasks, users, is_task_staff)
@pytest.mark.parametrize('org, project_id', [({'id': 2, 'slug': 'org2'}, 2)])
@pytest.mark.parametrize('role, is_staff, is_allow', [
('maintainer', False, True),
('supervisor', False, False),
])
def test_org_project_tasks_visibility(self, org, project_id, role, is_staff, is_allow, tasks, is_task_staff, is_project_staff, find_users):
users = find_users(org=org['id'], role=role)
tasks = list(filter(lambda x: x['project_id'] == project_id, tasks))
@pytest.mark.parametrize("org, project_id", [({"id": 2, "slug": "org2"}, 2)])
@pytest.mark.parametrize(
"role, is_staff, is_allow",
[
("maintainer", False, True),
("supervisor", False, False),
],
)
def test_org_project_tasks_visibility(
self,
org,
project_id,
role,
is_staff,
is_allow,
tasks,
is_task_staff,
is_project_staff,
find_users,
):
users = find_users(org=org["id"], role=role)
tasks = list(filter(lambda x: x["project_id"] == project_id, tasks))
assert len(tasks)
self._test_users_to_see_task_list(project_id, tasks, users, is_staff, is_allow, is_project_staff, org=org['slug'])
self._test_users_to_see_task_list(
project_id, tasks, users, is_staff, is_allow, is_project_staff, org=org["slug"]
)
@pytest.mark.parametrize('org, project_id, role', [
({'id': 2, 'slug': 'org2'}, 2, 'worker')
])
def test_org_task_assigneed_to_see_task(self, org, project_id, role, users, tasks, find_users, is_task_staff):
users = find_users(org=org['id'], role=role)
tasks = list(filter(lambda x: x['project_id'] == project_id and x['assignee'], tasks))
@pytest.mark.parametrize("org, project_id, role", [({"id": 2, "slug": "org2"}, 2, "worker")])
def test_org_task_assigneed_to_see_task(
self, org, project_id, role, users, tasks, find_users, is_task_staff
):
users = find_users(org=org["id"], role=role)
tasks = list(filter(lambda x: x["project_id"] == project_id and x["assignee"], tasks))
assert len(tasks)
self._test_assigned_users_to_see_task_data(tasks, users, is_task_staff, org=org['slug'])
self._test_assigned_users_to_see_task_data(tasks, users, is_task_staff, org=org["slug"])
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPostTasks:
def _test_create_task_201(self, user, spec, **kwargs):
with make_api_client(user) as api_client:
@ -116,22 +151,25 @@ class TestPostTasks:
def _test_create_task_403(self, user, spec, **kwargs):
with make_api_client(user) as api_client:
(_, response) = api_client.tasks_api.create(spec, **kwargs,
_parse_response=False, _check_status=False)
(_, response) = api_client.tasks_api.create(
spec, **kwargs, _parse_response=False, _check_status=False
)
assert response.status == HTTPStatus.FORBIDDEN
def _test_users_to_create_task_in_project(self, project_id, users, is_staff, is_allow, is_project_staff, **kwargs):
def _test_users_to_create_task_in_project(
self, project_id, users, is_staff, is_allow, is_project_staff, **kwargs
):
if is_staff:
users = [user for user in users if is_project_staff(user['id'], project_id) ]
users = [user for user in users if is_project_staff(user["id"], project_id)]
else:
users = [user for user in users if not is_project_staff(user['id'], project_id)]
users = [user for user in users if not is_project_staff(user["id"], project_id)]
assert len(users)
for user in users:
username = user['username']
username = user["username"]
spec = {
'name': f'test {username} to create a task within a project',
'project_id': project_id,
"name": f"test {username} to create a task within a project",
"project_id": project_id,
}
if is_allow:
@ -139,139 +177,159 @@ class TestPostTasks:
else:
self._test_create_task_403(username, spec, **kwargs)
@pytest.mark.parametrize('project_id', [1])
@pytest.mark.parametrize('groups, is_staff, is_allow', [
('admin', False, True),
('business', False, False),
('user', True, True),
])
def test_users_to_create_task_in_project(self, project_id, groups, is_staff, is_allow, is_project_staff, find_users):
@pytest.mark.parametrize("project_id", [1])
@pytest.mark.parametrize(
"groups, is_staff, is_allow",
[
("admin", False, True),
("business", False, False),
("user", True, True),
],
)
def test_users_to_create_task_in_project(
self, project_id, groups, is_staff, is_allow, is_project_staff, find_users
):
users = find_users(privilege=groups)
self._test_users_to_create_task_in_project(project_id, users, is_staff, is_allow, is_project_staff)
@pytest.mark.parametrize('org, project_id', [({'id': 2, 'slug': 'org2'}, 2)])
@pytest.mark.parametrize('role, is_staff, is_allow', [
('worker', False, False),
])
def test_worker_cannot_create_task_in_project_without_ownership(self, org, project_id, role, is_staff, is_allow, is_project_staff, find_users):
users = find_users(org=org['id'], role=role)
self._test_users_to_create_task_in_project(project_id, users, is_staff, is_allow, is_project_staff, org=org['slug'])
self._test_users_to_create_task_in_project(
project_id, users, is_staff, is_allow, is_project_staff
)
@pytest.mark.parametrize("org, project_id", [({"id": 2, "slug": "org2"}, 2)])
@pytest.mark.parametrize(
"role, is_staff, is_allow",
[
("worker", False, False),
],
)
def test_worker_cannot_create_task_in_project_without_ownership(
self, org, project_id, role, is_staff, is_allow, is_project_staff, find_users
):
users = find_users(org=org["id"], role=role)
self._test_users_to_create_task_in_project(
project_id, users, is_staff, is_allow, is_project_staff, org=org["slug"]
)
def test_can_create_task_with_skeleton(self):
username = "admin1"
spec = {
"name": f'test admin1 to create a task with skeleton',
"name": f"test admin1 to create a task with skeleton",
"labels": [
{
"name": "s1",
"color": "#5c5eba",
"attributes": [
{
"name": "color",
"mutable": False,
"input_type": "select",
"default_value": "white",
"values": [
"white",
"black"
]
}
],
"type": "skeleton",
"sublabels": [
{
"name": "1",
"color": "#d53957",
"name": "s1",
"color": "#5c5eba",
"attributes": [
{
"id": 23,
"name": "attr",
"mutable": False,
"input_type": "select",
"default_value": "val1",
"values": [
"val1",
"val2"
]
"name": "color",
"mutable": False,
"input_type": "select",
"default_value": "white",
"values": ["white", "black"],
}
],
"type": "points"
},
{
"name": "2",
"color": "#4925ec",
"attributes": [],
"type": "points"
},
{
"name": "3",
"color": "#59a8fe",
"attributes": [],
"type": "points"
}
],
"svg": "<line x1=\"36.329429626464844\" y1=\"45.98662185668945\" x2=\"59.07190704345703\" y2=\"23.076923370361328\" " \
"stroke=\"black\" data-type=\"edge\" data-node-from=\"2\" stroke-width=\"0.5\" data-node-to=\"3\"></line>" \
"<line x1=\"22.61705780029297\" y1=\"25.75250816345215\" x2=\"36.329429626464844\" y2=\"45.98662185668945\" " \
"stroke=\"black\" data-type=\"edge\" data-node-from=\"1\" stroke-width=\"0.5\" data-node-to=\"2\"></line>" \
"<circle r=\"1.5\" stroke=\"black\" fill=\"#b3b3b3\" cx=\"22.61705780029297\" cy=\"25.75250816345215\" " \
"stroke-width=\"0.1\" data-type=\"element node\" data-element-id=\"1\" data-node-id=\"1\" data-label-name=\"1\">" \
"</circle><circle r=\"1.5\" stroke=\"black\" fill=\"#b3b3b3\" cx=\"36.329429626464844\" cy=\"45.98662185668945\" " \
"stroke-width=\"0.1\" data-type=\"element node\" data-element-id=\"2\" data-node-id=\"2\" data-label-name=\"2\"></circle>" \
"<circle r=\"1.5\" stroke=\"black\" fill=\"#b3b3b3\" cx=\"59.07190704345703\" cy=\"23.076923370361328\" " \
"stroke-width=\"0.1\" data-type=\"element node\" data-element-id=\"3\" data-node-id=\"3\" data-label-name=\"3\"></circle>"
"type": "skeleton",
"sublabels": [
{
"name": "1",
"color": "#d53957",
"attributes": [
{
"id": 23,
"name": "attr",
"mutable": False,
"input_type": "select",
"default_value": "val1",
"values": ["val1", "val2"],
}
],
"type": "points",
},
{"name": "2", "color": "#4925ec", "attributes": [], "type": "points"},
{"name": "3", "color": "#59a8fe", "attributes": [], "type": "points"},
],
"svg": '<line x1="36.329429626464844" y1="45.98662185668945" x2="59.07190704345703" y2="23.076923370361328" '
'stroke="black" data-type="edge" data-node-from="2" stroke-width="0.5" data-node-to="3"></line>'
'<line x1="22.61705780029297" y1="25.75250816345215" x2="36.329429626464844" y2="45.98662185668945" '
'stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="2"></line>'
'<circle r="1.5" stroke="black" fill="#b3b3b3" cx="22.61705780029297" cy="25.75250816345215" '
'stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-name="1">'
'</circle><circle r="1.5" stroke="black" fill="#b3b3b3" cx="36.329429626464844" cy="45.98662185668945" '
'stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-name="2"></circle>'
'<circle r="1.5" stroke="black" fill="#b3b3b3" cx="59.07190704345703" cy="23.076923370361328" '
'stroke-width="0.1" data-type="element node" data-element-id="3" data-node-id="3" data-label-name="3"></circle>',
}
]
],
}
self._test_create_task_201(username, spec)
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetData:
_USERNAME = 'user1'
@pytest.mark.parametrize('content_type, task_id', [
('image/png', 8),
('image/png', 5),
('image/x.point-cloud-data', 6),
])
_USERNAME = "user1"
@pytest.mark.parametrize(
"content_type, task_id",
[
("image/png", 8),
("image/png", 5),
("image/x.point-cloud-data", 6),
],
)
def test_frame_content_type(self, content_type, task_id):
with make_api_client(self._USERNAME) as api_client:
(_, response) = api_client.tasks_api.retrieve_data(task_id,
type='frame', quality='original', number=0)
(_, response) = api_client.tasks_api.retrieve_data(
task_id, type="frame", quality="original", number=0
)
assert response.status == HTTPStatus.OK
assert response.headers['Content-Type'] == content_type
assert response.headers["Content-Type"] == content_type
@pytest.mark.usefixtures('changedb')
@pytest.mark.usefixtures("changedb")
class TestPatchTaskAnnotations:
def _test_check_response(self, is_allow, response, data=None):
if is_allow:
assert response.status == HTTPStatus.OK
assert DeepDiff(data, json.loads(response.data),
exclude_paths="root['version']") == {}
assert DeepDiff(data, json.loads(response.data), exclude_paths="root['version']") == {}
else:
assert response.status == HTTPStatus.FORBIDDEN
@pytest.fixture(scope='class')
@pytest.fixture(scope="class")
def request_data(self, annotations):
def get_data(tid):
data = deepcopy(annotations['task'][str(tid)])
data['shapes'][0].update({'points': [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]})
data['version'] += 1
data = deepcopy(annotations["task"][str(tid)])
data["shapes"][0].update({"points": [2.0, 3.0, 4.0, 5.0, 6.0, 7.0]})
data["version"] += 1
return data
return get_data
@pytest.mark.parametrize('org', [''])
@pytest.mark.parametrize('privilege, task_staff, is_allow', [
('admin', True, True), ('admin', False, True),
('business', True, True), ('business', False, False),
('worker', True, True), ('worker', False, False),
('user', True, True), ('user', False, False)
])
def test_user_update_task_annotations(self, org, privilege, task_staff, is_allow,
find_task_staff_user, find_users, request_data, tasks_by_org, filter_tasks_with_shapes):
@pytest.mark.parametrize("org", [""])
@pytest.mark.parametrize(
"privilege, task_staff, is_allow",
[
("admin", True, True),
("admin", False, True),
("business", True, True),
("business", False, False),
("worker", True, True),
("worker", False, False),
("user", True, True),
("user", False, False),
],
)
def test_user_update_task_annotations(
self,
org,
privilege,
task_staff,
is_allow,
find_task_staff_user,
find_users,
request_data,
tasks_by_org,
filter_tasks_with_shapes,
):
users = find_users(privilege=privilege)
tasks = tasks_by_org[org]
filtered_tasks = filter_tasks_with_shapes(tasks)
@ -280,21 +338,41 @@ class TestPatchTaskAnnotations:
data = request_data(tid)
with make_api_client(username) as api_client:
(_, response) = api_client.tasks_api.partial_update_annotations(
id=tid, action='update', org=org,
id=tid,
action="update",
org=org,
patched_labeled_data_request=deepcopy(data),
_parse_response=False, _check_status=False)
_parse_response=False,
_check_status=False,
)
self._test_check_response(is_allow, response, data)
@pytest.mark.parametrize('org', [2])
@pytest.mark.parametrize('role, task_staff, is_allow', [
('maintainer', False, True), ('owner', False, True),
('supervisor', False, False), ('worker', False, False),
('maintainer', True, True), ('owner', True, True),
('supervisor', True, True), ('worker', True, True)
])
def test_member_update_task_annotation(self, org, role, task_staff, is_allow,
find_task_staff_user, find_users, tasks_by_org, request_data):
@pytest.mark.parametrize("org", [2])
@pytest.mark.parametrize(
"role, task_staff, is_allow",
[
("maintainer", False, True),
("owner", False, True),
("supervisor", False, False),
("worker", False, False),
("maintainer", True, True),
("owner", True, True),
("supervisor", True, True),
("worker", True, True),
],
)
def test_member_update_task_annotation(
self,
org,
role,
task_staff,
is_allow,
find_task_staff_user,
find_users,
tasks_by_org,
request_data,
):
users = find_users(role=role, org=org)
tasks = tasks_by_org[org]
username, tid = find_task_staff_user(tasks, users, task_staff, [14])
@ -302,13 +380,18 @@ class TestPatchTaskAnnotations:
data = request_data(tid)
with make_api_client(username) as api_client:
(_, response) = api_client.tasks_api.partial_update_annotations(
id=tid, org_id=org, action='update',
id=tid,
org_id=org,
action="update",
patched_labeled_data_request=deepcopy(data),
_parse_response=False, _check_status=False)
_parse_response=False,
_check_status=False,
)
self._test_check_response(is_allow, response, data)
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetTaskDataset:
def _test_export_task(self, username, tid, **kwargs):
with make_api_client(username) as api_client:
@ -316,64 +399,69 @@ class TestGetTaskDataset:
def test_can_export_task_dataset(self, admin_user, tasks_with_shapes):
task = tasks_with_shapes[0]
response = self._test_export_task(admin_user, task['id'], format='CVAT for images 1.1')
response = self._test_export_task(admin_user, task["id"], format="CVAT for images 1.1")
assert response.data
@pytest.mark.usefixtures("changedb")
@pytest.mark.usefixtures("restore_cvat_data")
class TestPostTaskData:
_USERNAME = 'admin1'
_USERNAME = "admin1"
@staticmethod
def _wait_until_task_is_created(api: apis.TasksApi, task_id: int) -> models.RqStatus:
for _ in range(100):
(status, _) = api.retrieve_status(task_id)
if status.state.value in ['Finished', 'Failed']:
if status.state.value in ["Finished", "Failed"]:
return status
sleep(1)
raise Exception('Cannot create task')
raise Exception("Cannot create task")
def _test_create_task(self, username, spec, data, content_type, **kwargs):
with make_api_client(username) as api_client:
(task, response) = api_client.tasks_api.create(spec, **kwargs)
assert response.status == HTTPStatus.CREATED
(_, response) = api_client.tasks_api.create_data(task.id, data_request=deepcopy(data),
_content_type=content_type, **kwargs)
(_, response) = api_client.tasks_api.create_data(
task.id, data_request=deepcopy(data), _content_type=content_type, **kwargs
)
assert response.status == HTTPStatus.ACCEPTED
status = self._wait_until_task_is_created(api_client.tasks_api, task.id)
assert status.state.value == 'Finished'
assert status.state.value == "Finished"
return task.id
def test_can_create_task_with_defined_start_and_stop_frames(self):
task_spec = {
'name': f'test {self._USERNAME} to create a task with defined start and stop frames',
"labels": [{
"name": "car",
"color": "#ff00ff",
"attributes": [
{
"name": "a",
"mutable": True,
"input_type": "number",
"default_value": "5",
"values": ["4", "5", "6"]
}
]
}],
"name": f"test {self._USERNAME} to create a task with defined start and stop frames",
"labels": [
{
"name": "car",
"color": "#ff00ff",
"attributes": [
{
"name": "a",
"mutable": True,
"input_type": "number",
"default_value": "5",
"values": ["4", "5", "6"],
}
],
}
],
}
task_data = {
'image_quality': 75,
'start_frame': 2,
'stop_frame': 5,
'client_files': generate_image_files(7),
"image_quality": 75,
"start_frame": 2,
"stop_frame": 5,
"client_files": generate_image_files(7),
}
task_id = self._test_create_task(self._USERNAME, task_spec, task_data,
content_type="multipart/form-data")
task_id = self._test_create_task(
self._USERNAME, task_spec, task_data, content_type="multipart/form-data"
)
# check task size
with make_api_client(self._USERNAME) as api_client:
@ -382,7 +470,7 @@ class TestPostTaskData:
def test_can_get_annotations_from_new_task_with_skeletons(self):
spec = {
"name": f'test admin1 to create a task with skeleton',
"name": f"test admin1 to create a task with skeleton",
"labels": [
{
"name": "s1",
@ -390,36 +478,27 @@ class TestPostTaskData:
"attributes": [],
"type": "skeleton",
"sublabels": [
{
"name": "1",
"color": "#d12345",
"attributes": [],
"type": "points"
},
{
"name": "2",
"color": "#350dea",
"attributes": [],
"type": "points"
}
{"name": "1", "color": "#d12345", "attributes": [], "type": "points"},
{"name": "2", "color": "#350dea", "attributes": [], "type": "points"},
],
"svg": "<line x1=\"19.464284896850586\" y1=\"21.922269821166992\" x2=\"54.08613586425781\" y2=\"43.60293960571289\" " \
"stroke=\"black\" data-type=\"edge\" data-node-from=\"1\" stroke-width=\"0.5\" data-node-to=\"2\"></line>" \
"<circle r=\"1.5\" stroke=\"black\" fill=\"#b3b3b3\" cx=\"19.464284896850586\" cy=\"21.922269821166992\" " \
"stroke-width=\"0.1\" data-type=\"element node\" data-element-id=\"1\" data-node-id=\"1\" data-label-id=\"103\"></circle>" \
"<circle r=\"1.5\" stroke=\"black\" fill=\"#b3b3b3\" cx=\"54.08613586425781\" cy=\"43.60293960571289\" " \
"stroke-width=\"0.1\" data-type=\"element node\" data-element-id=\"2\" data-node-id=\"2\" data-label-id=\"104\"></circle>"
"svg": '<line x1="19.464284896850586" y1="21.922269821166992" x2="54.08613586425781" y2="43.60293960571289" '
'stroke="black" data-type="edge" data-node-from="1" stroke-width="0.5" data-node-to="2"></line>'
'<circle r="1.5" stroke="black" fill="#b3b3b3" cx="19.464284896850586" cy="21.922269821166992" '
'stroke-width="0.1" data-type="element node" data-element-id="1" data-node-id="1" data-label-id="103"></circle>'
'<circle r="1.5" stroke="black" fill="#b3b3b3" cx="54.08613586425781" cy="43.60293960571289" '
'stroke-width="0.1" data-type="element node" data-element-id="2" data-node-id="2" data-label-id="104"></circle>',
}
]
],
}
task_data = {
'image_quality': 75,
'client_files': generate_image_files(3),
"image_quality": 75,
"client_files": generate_image_files(3),
}
task_id = self._test_create_task(self._USERNAME, spec, task_data,
content_type="multipart/form-data")
task_id = self._test_create_task(
self._USERNAME, spec, task_data, content_type="multipart/form-data"
)
response = get_method(self._USERNAME, f"tasks/{task_id}")
label_ids = {}
@ -428,60 +507,8 @@ class TestPostTaskData:
job_id = response.json()["segments"][0]["jobs"][0]["id"]
patch_data = {
"shapes": [{
"type": "skeleton",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [],
"frame": 0,
"label_id": label_ids["skeleton"][0],
"group": 0,
"source": "manual",
"attributes": [],
"elements": [
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [
131.63947368421032,
165.0868421052637
],
"frame": 0,
"label_id": label_ids["points"][0],
"group": 0,
"source": "manual",
"attributes": []
},
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [
354.98157894736823,
304.2710526315795
],
"frame": 0,
"label_id": label_ids["points"][1],
"group": 0,
"source": "manual",
"attributes": []
}
]
}],
"tracks": [{
"frame": 0,
"label_id": label_ids["skeleton"][0],
"group": 0,
"source": "manual",
"shapes": [
{
"shapes": [
{
"type": "skeleton",
"occluded": False,
"outside": False,
@ -489,90 +516,146 @@ class TestPostTaskData:
"rotation": 0,
"points": [],
"frame": 0,
"attributes": []
}
],
"attributes": [],
"elements": [
{
"frame": 0,
"label_id": label_ids["points"][0],
"label_id": label_ids["skeleton"][0],
"group": 0,
"source": "manual",
"shapes": [
"attributes": [],
"elements": [
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [
295.6394736842103,
472.5868421052637
],
"frame": 0,
"attributes": []
}
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [131.63947368421032, 165.0868421052637],
"frame": 0,
"label_id": label_ids["points"][0],
"group": 0,
"source": "manual",
"attributes": [],
},
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [354.98157894736823, 304.2710526315795],
"frame": 0,
"label_id": label_ids["points"][1],
"group": 0,
"source": "manual",
"attributes": [],
},
],
"attributes": []
},
{
}
],
"tracks": [
{
"frame": 0,
"label_id": label_ids["points"][1],
"label_id": label_ids["skeleton"][0],
"group": 0,
"source": "manual",
"shapes": [
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [
619.3236842105262,
846.9815789473689
],
"frame": 0,
"attributes": []
"type": "skeleton",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [],
"frame": 0,
"attributes": [],
}
],
"attributes": []
}
]
}],
"attributes": [],
"elements": [
{
"frame": 0,
"label_id": label_ids["points"][0],
"group": 0,
"source": "manual",
"shapes": [
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [295.6394736842103, 472.5868421052637],
"frame": 0,
"attributes": [],
}
],
"attributes": [],
},
{
"frame": 0,
"label_id": label_ids["points"][1],
"group": 0,
"source": "manual",
"shapes": [
{
"type": "points",
"occluded": False,
"outside": False,
"z_order": 0,
"rotation": 0,
"points": [619.3236842105262, 846.9815789473689],
"frame": 0,
"attributes": [],
}
],
"attributes": [],
},
],
}
],
"tags": [],
"version": 0
"version": 0,
}
response = patch_method(self._USERNAME, f"jobs/{job_id}/annotations", patch_data, action="create")
response = patch_method(
self._USERNAME, f"jobs/{job_id}/annotations", patch_data, action="create"
)
response = get_method(self._USERNAME, f"jobs/{job_id}/annotations")
assert response.status_code == HTTPStatus.OK
@pytest.mark.parametrize('cloud_storage_id, manifest, use_bucket_content, org', [
(1, 'manifest.jsonl', False, ''), # public bucket
(2, 'sub/manifest.jsonl', True, 'org2'), # private bucket
])
def test_create_task_with_cloud_storage_files(self, cloud_storage_id, manifest, use_bucket_content, org):
@pytest.mark.parametrize(
"cloud_storage_id, manifest, use_bucket_content, org",
[
(1, "manifest.jsonl", False, ""), # public bucket
(2, "sub/manifest.jsonl", True, "org2"), # private bucket
],
)
def test_create_task_with_cloud_storage_files(
self, cloud_storage_id, manifest, use_bucket_content, org
):
if use_bucket_content:
cloud_storage_content = get_cloud_storage_content(self._USERNAME, cloud_storage_id, manifest)
cloud_storage_content = get_cloud_storage_content(
self._USERNAME, cloud_storage_id, manifest
)
else:
cloud_storage_content = ['image_case_65_1.png', 'image_case_65_2.png']
cloud_storage_content = ["image_case_65_1.png", "image_case_65_2.png"]
cloud_storage_content.append(manifest)
task_spec = {
"name": f"Task with files from cloud storage {cloud_storage_id}",
"labels": [{
"name": "car",
}],
"labels": [
{
"name": "car",
}
],
}
data_spec = {
'image_quality': 75,
'use_cache': True,
'storage': 'cloud_storage',
'cloud_storage_id': cloud_storage_id,
'server_files': cloud_storage_content,
"image_quality": 75,
"use_cache": True,
"storage": "cloud_storage",
"cloud_storage_id": cloud_storage_id,
"server_files": cloud_storage_content,
}
self._test_create_task(self._USERNAME, task_spec, data_spec,
content_type="application/json", org=org)
self._test_create_task(
self._USERNAME, task_spec, data_spec, content_type="application/json", org=org
)

@ -3,81 +3,91 @@
#
# SPDX-License-Identifier: MIT
from http import HTTPStatus
import json
import typing
from cvat_sdk.core.helpers import get_paginated_collection
from http import HTTPStatus
import pytest
from cvat_sdk.core.helpers import get_paginated_collection
from deepdiff import DeepDiff
from shared.utils.config import make_api_client
@pytest.mark.usefixtures('dontchangedb')
@pytest.mark.usefixtures("dontchangedb")
class TestGetUsers:
def _test_can_see(self, user, data,
id_: typing.Union[typing.Literal['self'], int, None] = None,
*,
exclude_paths='', **kwargs):
def _test_can_see(
self,
user,
data,
id_: typing.Union[typing.Literal["self"], int, None] = None,
*,
exclude_paths="",
**kwargs,
):
with make_api_client(user) as api_client:
# TODO: refactor into several functions
if id_ == 'self':
(_, response) = api_client.users_api.retrieve_self(**kwargs,
_parse_response=False)
if id_ == "self":
(_, response) = api_client.users_api.retrieve_self(**kwargs, _parse_response=False)
assert response.status == HTTPStatus.OK
response_data = json.loads(response.data)
elif id_ is None:
response_data = get_paginated_collection(api_client.users_api.list_endpoint,
return_json=True, **kwargs)
response_data = get_paginated_collection(
api_client.users_api.list_endpoint, return_json=True, **kwargs
)
else:
(_, response) = api_client.users_api.retrieve(id_, **kwargs,
_parse_response=False)
(_, response) = api_client.users_api.retrieve(id_, **kwargs, _parse_response=False)
assert response.status == HTTPStatus.OK
response_data = json.loads(response.data)
assert DeepDiff(data, response_data, ignore_order=True,
exclude_paths=exclude_paths) == {}
assert DeepDiff(data, response_data, ignore_order=True, exclude_paths=exclude_paths) == {}
def _test_cannot_see(self, user, id_: typing.Union[typing.Literal['self'], int, None] = None,
**kwargs):
def _test_cannot_see(
self, user, id_: typing.Union[typing.Literal["self"], int, None] = None, **kwargs
):
with make_api_client(user) as api_client:
# TODO: refactor into several functions
if id_ == 'self':
(_, response) = api_client.users_api.retrieve_self(**kwargs,
_parse_response=False, _check_status=False)
if id_ == "self":
(_, response) = api_client.users_api.retrieve_self(
**kwargs, _parse_response=False, _check_status=False
)
elif id_ is None:
(_, response) = api_client.users_api.list(**kwargs,
_parse_response=False, _check_status=False)
(_, response) = api_client.users_api.list(
**kwargs, _parse_response=False, _check_status=False
)
else:
(_, response) = api_client.users_api.retrieve(id_, **kwargs,
_parse_response=False, _check_status=False)
(_, response) = api_client.users_api.retrieve(
id_, **kwargs, _parse_response=False, _check_status=False
)
assert response.status == HTTPStatus.FORBIDDEN
def test_admin_can_see_all_others(self, users):
exclude_paths = [f"root[{i}]['last_login']" for i in range(len(users))]
self._test_can_see('admin2', users.raw, exclude_paths=exclude_paths)
self._test_can_see("admin2", users.raw, exclude_paths=exclude_paths)
def test_everybody_can_see_self(self, users_by_name):
for user, data in users_by_name.items():
self._test_can_see(user, data, id_="self", exclude_paths="root['last_login']")
def test_non_members_cannot_see_list_of_members(self):
self._test_cannot_see('user2', org='org1')
self._test_cannot_see("user2", org="org1")
def test_non_admin_cannot_see_others(self, users):
non_admins = (v for v in users if not v['is_superuser'])
user = next(non_admins)['username']
user_id = next(non_admins)['id']
non_admins = (v for v in users if not v["is_superuser"])
user = next(non_admins)["username"]
user_id = next(non_admins)["id"]
self._test_cannot_see(user, id_=user_id)
def test_all_members_can_see_list_of_members(self, find_users, users):
org_members = [user['username'] for user in find_users(org=1)]
available_fields = ['url', 'id', 'username', 'first_name', 'last_name']
org_members = [user["username"] for user in find_users(org=1)]
available_fields = ["url", "id", "username", "first_name", "last_name"]
data = [dict(filter(lambda row: row[0] in available_fields, user.items()))
for user in users if user['username'] in org_members]
data = [
dict(filter(lambda row: row[0] in available_fields, user.items()))
for user in users
if user["username"] in org_members
]
for member in org_members:
self._test_can_see(member, data, org='org1')
self._test_can_see(member, data, org="org1")

File diff suppressed because it is too large Load Diff

@ -2,17 +2,20 @@
#
# SPDX-License-Identifier: MIT
import pytest
import json
import os.path as osp
import pytest
from shared.utils.config import ASSETS_DIR
CVAT_DB_DIR = osp.join(ASSETS_DIR, 'cvat_db')
CVAT_DB_DIR = osp.join(ASSETS_DIR, "cvat_db")
class Container:
def __init__(self, data, key='id'):
def __init__(self, data, key="id"):
self.raw_data = data
self.map_data = { obj[key]: obj for obj in data }
self.map_data = {obj[key]: obj for obj in data}
@property
def raw(self):
@ -33,153 +36,186 @@ class Container:
return self.raw_data[key]
return self.map_data[key]
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def users():
with open(osp.join(ASSETS_DIR, 'users.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "users.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def organizations():
with open(osp.join(ASSETS_DIR, 'organizations.json')) as f:
with open(osp.join(ASSETS_DIR, "organizations.json")) as f:
return Container(json.load(f))
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def memberships():
with open(osp.join(ASSETS_DIR, 'memberships.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "memberships.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def tasks():
with open(osp.join(ASSETS_DIR, 'tasks.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "tasks.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def projects():
with open(osp.join(ASSETS_DIR, 'projects.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "projects.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def jobs():
with open(osp.join(ASSETS_DIR, 'jobs.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "jobs.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def invitations():
with open(osp.join(ASSETS_DIR, 'invitations.json')) as f:
return Container(json.load(f)['results'], key='key')
with open(osp.join(ASSETS_DIR, "invitations.json")) as f:
return Container(json.load(f)["results"], key="key")
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def annotations():
with open(osp.join(ASSETS_DIR, 'annotations.json')) as f:
with open(osp.join(ASSETS_DIR, "annotations.json")) as f:
return json.load(f)
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def cloud_storages():
with open(osp.join(ASSETS_DIR, 'cloudstorages.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "cloudstorages.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def issues():
with open(osp.join(ASSETS_DIR, 'issues.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "issues.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def webhooks():
with open(osp.join(ASSETS_DIR, 'webhooks.json')) as f:
return Container(json.load(f)['results'])
with open(osp.join(ASSETS_DIR, "webhooks.json")) as f:
return Container(json.load(f)["results"])
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def users_by_name(users):
return {user['username']: user for user in users}
return {user["username"]: user for user in users}
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def jobs_by_org(tasks, jobs):
data = {}
for job in jobs:
data.setdefault(tasks[job['task_id']]['organization'], []).append(job)
data[''] = data.pop(None, [])
data.setdefault(tasks[job["task_id"]]["organization"], []).append(job)
data[""] = data.pop(None, [])
return data
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def projects_by_org(projects):
data = {}
for project in projects:
data.setdefault(project['organization'], []).append(project)
data[''] = data.pop(None, [])
data.setdefault(project["organization"], []).append(project)
data[""] = data.pop(None, [])
return data
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def tasks_by_org(tasks):
data = {}
for task in tasks:
data.setdefault(task['organization'], []).append(task)
data[''] = data.pop(None, [])
data.setdefault(task["organization"], []).append(task)
data[""] = data.pop(None, [])
return data
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def issues_by_org(tasks, jobs, issues):
data = {}
for issue in issues:
data.setdefault(tasks[jobs[issue['job']]['task_id']]['organization'], []).append(issue)
data[''] = data.pop(None, [])
data.setdefault(tasks[jobs[issue["job"]]["task_id"]]["organization"], []).append(issue)
data[""] = data.pop(None, [])
return data
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def assignee_id():
def get_id(data):
if data.get('assignee') is not None:
return data['assignee']['id']
if data.get("assignee") is not None:
return data["assignee"]["id"]
return get_id
def ownership(func):
def wrap(user_id, resource_id):
if resource_id is None:
return False
return func(user_id, resource_id)
return wrap
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_project_staff(projects, assignee_id):
@ownership
def check(user_id, pid):
return user_id == projects[pid]['owner']['id'] or \
user_id == assignee_id(projects[pid])
return user_id == projects[pid]["owner"]["id"] or user_id == assignee_id(projects[pid])
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_task_staff(tasks, is_project_staff, assignee_id):
@ownership
def check(user_id, tid):
return user_id == tasks[tid]['owner']['id'] or \
user_id == assignee_id(tasks[tid]) or \
is_project_staff(user_id, tasks[tid]['project_id'])
return (
user_id == tasks[tid]["owner"]["id"]
or user_id == assignee_id(tasks[tid])
or is_project_staff(user_id, tasks[tid]["project_id"])
)
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_job_staff(jobs, is_task_staff, assignee_id):
@ownership
def check(user_id, jid):
return user_id == assignee_id(jobs[jid]) or \
is_task_staff(user_id, jobs[jid]['task_id'])
return user_id == assignee_id(jobs[jid]) or is_task_staff(user_id, jobs[jid]["task_id"])
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_issue_staff(issues, jobs, assignee_id):
@ownership
def check(user_id, issue_id):
return user_id == issues[issue_id]['owner']['id'] or \
user_id == assignee_id(issues[issue_id]) or \
user_id == assignee_id(jobs[issues[issue_id]['job']])
return (
user_id == issues[issue_id]["owner"]["id"]
or user_id == assignee_id(issues[issue_id])
or user_id == assignee_id(jobs[issues[issue_id]["job"]])
)
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_issue_admin(issues, jobs, is_task_staff):
@ownership
def check(user_id, issue_id):
return is_task_staff(user_id, jobs[issues[issue_id]['job']]['task_id'])
return is_task_staff(user_id, jobs[issues[issue_id]["job"]]["task_id"])
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def find_users(test_db):
def find(**kwargs):
assert len(kwargs) > 0
@ -188,115 +224,149 @@ def find_users(test_db):
data = test_db
kwargs = dict(filter(lambda a: a[1] is not None, kwargs.items()))
for field, value in kwargs.items():
if field.startswith('exclude_'):
field = field.split('_', maxsplit=1)[1]
exclude_rows = set(v['id'] for v in
filter(lambda a: a[field] == value, test_db))
data = list(filter(lambda a: a['id'] not in exclude_rows, data))
if field.startswith("exclude_"):
field = field.split("_", maxsplit=1)[1]
exclude_rows = set(v["id"] for v in filter(lambda a: a[field] == value, test_db))
data = list(filter(lambda a: a["id"] not in exclude_rows, data))
else:
data = list(filter(lambda a: a[field] == value, data))
return data
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def test_db(users, users_by_name, memberships):
data = []
fields = ['username', 'id', 'privilege', 'role', 'org', 'membership_id']
fields = ["username", "id", "privilege", "role", "org", "membership_id"]
def add_row(**kwargs):
data.append({field: kwargs.get(field) for field in fields})
for user in users:
for group in user['groups']:
add_row(username=user['username'], id=user['id'], privilege=group)
for group in user["groups"]:
add_row(username=user["username"], id=user["id"], privilege=group)
for membership in memberships:
username = membership['user']['username']
for group in users_by_name[username]['groups']:
add_row(username=username, role=membership['role'], privilege=group,
id=membership['user']['id'], org=membership['organization'],
membership_id=membership['id'])
username = membership["user"]["username"]
for group in users_by_name[username]["groups"]:
add_row(
username=username,
role=membership["role"],
privilege=group,
id=membership["user"]["id"],
org=membership["organization"],
membership_id=membership["id"],
)
return data
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def org_staff(memberships):
def find(org_id):
if org_id in ['', None]:
if org_id in ["", None]:
return set()
else:
return set(m['user']['id'] for m in memberships
if m['role'] in ['maintainer', 'owner'] and m['user'] is not None
and m['organization'] == org_id)
return set(
m["user"]["id"]
for m in memberships
if m["role"] in ["maintainer", "owner"]
and m["user"] is not None
and m["organization"] == org_id
)
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def is_org_member(memberships):
def check(user_id, org_id):
if org_id in ['', None]:
if org_id in ["", None]:
return True
else:
return user_id in set(m['user']['id'] for m in memberships
if m['user'] is not None and m['organization'] == org_id)
return user_id in set(
m["user"]["id"]
for m in memberships
if m["user"] is not None and m["organization"] == org_id
)
return check
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def find_job_staff_user(is_job_staff):
def find(jobs, users, is_staff, wo_jobs=None):
for job in jobs:
if wo_jobs is not None and job['id'] in wo_jobs:
if wo_jobs is not None and job["id"] in wo_jobs:
continue
for user in users:
if is_staff == is_job_staff(user['id'], job['id']):
return user['username'], job['id']
if is_staff == is_job_staff(user["id"], job["id"]):
return user["username"], job["id"]
return None, None
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def find_task_staff_user(is_task_staff):
def find(tasks, users, is_staff, wo_tasks=None):
for task in tasks:
if wo_tasks is not None and task['id'] in wo_tasks:
if wo_tasks is not None and task["id"] in wo_tasks:
continue
for user in users:
if is_staff == is_task_staff(user['id'], task['id']):
return user['username'], task['id']
if is_staff == is_task_staff(user["id"], task["id"]):
return user["username"], task["id"]
return None, None
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def find_issue_staff_user(is_issue_staff, is_issue_admin):
def find(issues, users, is_staff, is_admin):
for issue in issues:
for user in users:
i_admin, i_staff = is_issue_admin(user['id'], issue['id']), is_issue_staff(user['id'], issue['id'])
if (is_admin is None and (i_staff or i_admin) == is_staff) \
or (is_admin == i_admin and is_staff == i_staff):
return user['username'], issue['id']
i_admin, i_staff = is_issue_admin(user["id"], issue["id"]), is_issue_staff(
user["id"], issue["id"]
)
if (is_admin is None and (i_staff or i_admin) == is_staff) or (
is_admin == i_admin and is_staff == i_staff
):
return user["username"], issue["id"]
return None, None
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def filter_jobs_with_shapes(annotations):
def find(jobs):
return list(filter(lambda j: annotations['job'][str(j['id'])]['shapes'], jobs))
return list(filter(lambda j: annotations["job"][str(j["id"])]["shapes"], jobs))
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def filter_tasks_with_shapes(annotations):
def find(tasks):
return list(filter(lambda t: annotations['task'][str(t['id'])]['shapes'], tasks))
return list(filter(lambda t: annotations["task"][str(t["id"])]["shapes"], tasks))
return find
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def jobs_with_shapes(jobs, filter_jobs_with_shapes):
return filter_jobs_with_shapes(jobs)
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def tasks_with_shapes(tasks, filter_tasks_with_shapes):
return filter_tasks_with_shapes(tasks)
@pytest.fixture(scope='session')
@pytest.fixture(scope="session")
def admin_user(users):
for user in users:
if user["is_superuser"] and user["is_active"]:

@ -2,6 +2,7 @@
#
# SPDX-License-Identifier: MIT
import os
import os.path as osp
import re
from http import HTTPStatus
@ -9,8 +10,8 @@ from subprocess import PIPE, CalledProcessError, run
from time import sleep
import pytest
import os
import requests
from shared.utils.config import ASSETS_DIR, get_api_url
CVAT_ROOT_DIR = __file__[: __file__.rfind(osp.join("tests", ""))]
@ -30,7 +31,7 @@ DC_FILES = [
for dc_file in (
"docker-compose.dev.yml",
"tests/docker-compose.minio.yml",
"tests/docker-compose.webhook.yml"
"tests/docker-compose.webhook.yml",
)
] + CONTAINER_NAME_FILES
@ -95,12 +96,16 @@ def _run(command, capture_output=True):
"Add `-s` option to see more details"
)
def _kube_get_server_pod_name():
output, _ = _run("kubectl get pods -l component=server -o jsonpath={.items[0].metadata.name}")
return output
def _kube_get_db_pod_name():
output, _ = _run("kubectl get pods -l app.kubernetes.io/name=postgresql -o jsonpath={.items[0].metadata.name}")
output, _ = _run(
"kubectl get pods -l app.kubernetes.io/name=postgresql -o jsonpath={.items[0].metadata.name}"
)
return output
@ -131,30 +136,35 @@ def kube_exec_cvat_db(command):
def docker_restore_db():
docker_exec_cvat_db(
"psql -U root -d postgres -v from=test_db -v to=cvat -f /tmp/restore.sql"
)
docker_exec_cvat_db("psql -U root -d postgres -v from=test_db -v to=cvat -f /tmp/restore.sql")
def kube_restore_db():
kube_exec_cvat_db(
["/bin/sh", "-c", "PGPASSWORD=cvat_postgresql_postgres psql -U postgres -d postgres -v from=test_db -v to=cvat -f /tmp/restore.sql"]
[
"/bin/sh",
"-c",
"PGPASSWORD=cvat_postgresql_postgres psql -U postgres -d postgres -v from=test_db -v to=cvat -f /tmp/restore.sql",
]
)
def running_containers():
return [cn for cn in _run("docker ps --format {{.Names}}")[0].split("\n") if cn]
def dump_db():
if 'test_cvat_server_1' not in running_containers():
if "test_cvat_server_1" not in running_containers():
pytest.exit("CVAT is not running")
with open(osp.join(CVAT_DB_DIR, "data.json"), "w") as f:
try:
run( # nosec
run( # nosec
"docker exec test_cvat_server_1 \
python manage.py dumpdata \
--indent 2 --natural-foreign \
--exclude=auth.permission --exclude=contenttypes".split(),
stdout=f, check=True
stdout=f,
check=True,
)
except CalledProcessError:
pytest.exit("Database dump failed.\n")
@ -162,15 +172,9 @@ def dump_db():
def create_compose_files():
for filename in CONTAINER_NAME_FILES:
with open(filename.replace(".tests.yml", ".yml"), "r") as dcf, open(
filename, "w"
) as ndcf:
with open(filename.replace(".tests.yml", ".yml"), "r") as dcf, open(filename, "w") as ndcf:
ndcf.writelines(
[
line
for line in dcf.readlines()
if not re.match("^.+container_name.+$", line)
]
[line for line in dcf.readlines() if not re.match("^.+container_name.+$", line)]
)
@ -195,6 +199,7 @@ def docker_restore_data_volumes():
)
docker_exec_cvat("tar --strip 3 -xjf /tmp/cvat_data.tar.bz2 -C /home/django/data/")
def kube_restore_data_volumes():
pod_name = _kube_get_server_pod_name()
kube_cp(
@ -213,16 +218,15 @@ def start_services(rebuild=False):
_run(
f"docker-compose -p {PREFIX} "
+ "--env-file " + osp.join(CVAT_ROOT_DIR, "tests", "python", "webhook_receiver", ".env")
+ "--env-file "
+ osp.join(CVAT_ROOT_DIR, "tests", "python", "webhook_receiver", ".env")
+ f" -f {' -f '.join(DC_FILES)} up -d "
+ "--build" * rebuild,
capture_output=False,
)
docker_restore_data_volumes()
docker_cp(
osp.join(CVAT_DB_DIR, "restore.sql"), f"{PREFIX}_cvat_db_1:/tmp/restore.sql"
)
docker_cp(osp.join(CVAT_DB_DIR, "restore.sql"), f"{PREFIX}_cvat_db_1:/tmp/restore.sql")
docker_cp(osp.join(CVAT_DB_DIR, "data.json"), f"{PREFIX}_cvat_server_1:/tmp/data.json")
@ -235,11 +239,13 @@ def services(request):
dumpdb = request.config.getoption("--dumpdb")
platform = request.config.getoption("--platform")
if platform == 'kube' and any((stop, start, rebuild, cleanup, dumpdb)):
raise Exception('''--platform=kube is not compatible with any of the other options
--stop-services --start-services --rebuild --cleanup --dumpdb''')
if platform == "kube" and any((stop, start, rebuild, cleanup, dumpdb)):
raise Exception(
"""--platform=kube is not compatible with any of the other options
--stop-services --start-services --rebuild --cleanup --dumpdb"""
)
if platform == 'local':
if platform == "local":
if start and stop:
raise Exception("--start-services and --stop-services are incompatible")
@ -258,7 +264,8 @@ def services(request):
if stop:
_run(
f"docker-compose -p {PREFIX} "
+ "--env-file " + osp.join(CVAT_ROOT_DIR, "tests", "python", "webhook_receiver", ".env")
+ "--env-file "
+ osp.join(CVAT_ROOT_DIR, "tests", "python", "webhook_receiver", ".env")
+ f" -f {' -f '.join(DC_FILES)} down -v",
capture_output=False,
)
@ -273,22 +280,18 @@ def services(request):
)
if start:
pytest.exit(
"All necessary containers have been created and started.", returncode=0
)
pytest.exit("All necessary containers have been created and started.", returncode=0)
yield
docker_restore_db()
docker_exec_cvat_db("dropdb test_db")
elif platform == 'kube':
elif platform == "kube":
kube_restore_data_volumes()
server_pod_name = _kube_get_server_pod_name()
db_pod_name = _kube_get_db_pod_name()
kube_cp(
osp.join(CVAT_DB_DIR, "restore.sql"), f"{db_pod_name}:/tmp/restore.sql"
)
kube_cp(osp.join(CVAT_DB_DIR, "restore.sql"), f"{db_pod_name}:/tmp/restore.sql")
kube_cp(osp.join(CVAT_DB_DIR, "data.json"), f"{server_pod_name}:/tmp/data.json")
wait_for_server()
@ -296,7 +299,11 @@ def services(request):
kube_exec_cvat("python manage.py loaddata /tmp/data.json")
kube_exec_cvat_db(
["/bin/sh", "-c", "PGPASSWORD=cvat_postgresql_postgres psql -U postgres -d postgres -v from=cvat -v to=test_db -f /tmp/restore.sql"]
[
"/bin/sh",
"-c",
"PGPASSWORD=cvat_postgresql_postgres psql -U postgres -d postgres -v from=cvat -v to=test_db -f /tmp/restore.sql",
]
)
yield

@ -4,6 +4,7 @@
import io
import logging
import pytest

@ -3,51 +3,66 @@
# SPDX-License-Identifier: MIT
import os.path as osp
import requests
from cvat_sdk.api_client import ApiClient, Configuration
ROOT_DIR = __file__[:__file__.rfind(osp.join("utils", ""))]
ASSETS_DIR = osp.abspath(osp.join(ROOT_DIR, 'assets'))
ROOT_DIR = __file__[: __file__.rfind(osp.join("utils", ""))]
ASSETS_DIR = osp.abspath(osp.join(ROOT_DIR, "assets"))
# Suppress the warning from Bandit about hardcoded passwords
USER_PASS = '!Q@W#E$R' # nosec
BASE_URL = 'http://localhost:8080'
API_URL = BASE_URL + '/api/'
USER_PASS = "!Q@W#E$R" # nosec
BASE_URL = "http://localhost:8080"
API_URL = BASE_URL + "/api/"
# MiniIO settings
MINIO_KEY = 'minio_access_key'
MINIO_SECRET_KEY = 'minio_secret_key' # nosec
MINIO_ENDPOINT_URL = 'http://localhost:9000'
MINIO_KEY = "minio_access_key"
MINIO_SECRET_KEY = "minio_secret_key" # nosec
MINIO_ENDPOINT_URL = "http://localhost:9000"
def _to_query_params(**kwargs):
return '&'.join([f'{k}={v}' for k,v in kwargs.items()])
return "&".join([f"{k}={v}" for k, v in kwargs.items()])
def get_server_url(endpoint, **kwargs):
return BASE_URL + '/' + endpoint + '?' + _to_query_params(**kwargs)
return BASE_URL + "/" + endpoint + "?" + _to_query_params(**kwargs)
def get_api_url(endpoint, **kwargs):
return API_URL + endpoint + '?' + _to_query_params(**kwargs)
return API_URL + endpoint + "?" + _to_query_params(**kwargs)
def get_method(username, endpoint, **kwargs):
return requests.get(get_api_url(endpoint, **kwargs), auth=(username, USER_PASS))
def options_method(username, endpoint, **kwargs):
return requests.options(get_api_url(endpoint, **kwargs), auth=(username, USER_PASS))
def delete_method(username, endpoint, **kwargs):
return requests.delete(get_api_url(endpoint, **kwargs), auth=(username, USER_PASS))
def patch_method(username, endpoint, data, **kwargs):
return requests.patch(get_api_url(endpoint, **kwargs), json=data, auth=(username, USER_PASS))
def post_method(username, endpoint, data, **kwargs):
return requests.post(get_api_url(endpoint, **kwargs), json=data, auth=(username, USER_PASS))
def post_files_method(username, endpoint, data, files, **kwargs):
return requests.post(get_api_url(endpoint, **kwargs), data=data, files=files, auth=(username, USER_PASS))
return requests.post(
get_api_url(endpoint, **kwargs), data=data, files=files, auth=(username, USER_PASS)
)
def server_get(username, endpoint, **kwargs):
return requests.get(get_server_url(endpoint, **kwargs), auth=(username, USER_PASS))
def make_api_client(user: str, *, password: str = None) -> ApiClient:
return ApiClient(configuration=Configuration(host=BASE_URL,
username=user, password=password or USER_PASS))
return ApiClient(
configuration=Configuration(host=BASE_URL, username=user, password=password or USER_PASS)
)

@ -2,24 +2,35 @@
#
# SPDX-License-Identifier: MIT
import os.path as osp
from config import get_method, ASSETS_DIR
import json
import os.path as osp
from config import ASSETS_DIR, get_method
if __name__ == '__main__':
if __name__ == "__main__":
annotations = {}
for obj in ['user', 'project', 'task', 'job', 'organization', 'membership',
'invitation', 'cloudstorage', 'issue', 'webhook']:
response = get_method('admin1', f'{obj}s', page_size='all')
with open(osp.join(ASSETS_DIR, f'{obj}s.json'), 'w') as f:
for obj in [
"user",
"project",
"task",
"job",
"organization",
"membership",
"invitation",
"cloudstorage",
"issue",
"webhook",
]:
response = get_method("admin1", f"{obj}s", page_size="all")
with open(osp.join(ASSETS_DIR, f"{obj}s.json"), "w") as f:
json.dump(response.json(), f, indent=2, sort_keys=True)
if obj in ['job', 'task']:
if obj in ["job", "task"]:
annotations[obj] = {}
for _obj in response.json()['results']:
for _obj in response.json()["results"]:
oid = _obj["id"]
response = get_method('admin1', f'{obj}s/{oid}/annotations')
response = get_method("admin1", f"{obj}s/{oid}/annotations")
annotations[obj][oid] = response.json()
with open(osp.join(ASSETS_DIR, f'annotations.json'), 'w') as f:
with open(osp.join(ASSETS_DIR, f"annotations.json"), "w") as f:
json.dump(annotations, f, indent=2, sort_keys=True)

@ -2,24 +2,26 @@
#
# SPDX-License-Identifier: MIT
from io import BytesIO
from typing import List
from PIL import Image
from io import BytesIO
def generate_image_file(filename='image.png', size=(50, 50)):
def generate_image_file(filename="image.png", size=(50, 50)):
f = BytesIO()
image = Image.new('RGB', size=size)
image.save(f, 'jpeg')
image = Image.new("RGB", size=size)
image.save(f, "jpeg")
f.name = filename
f.seek(0)
return f
def generate_image_files(count) -> List[BytesIO]:
images = []
for i in range(count):
image = generate_image_file(f'{i}.jpeg')
image = generate_image_file(f"{i}.jpeg")
images.append(image)
return images

@ -2,8 +2,8 @@
#
# SPDX-License-Identifier: MIT
import re
import os
import re
from http import HTTPStatus
from http.server import BaseHTTPRequestHandler, HTTPServer

Loading…
Cancel
Save