[WEB-5044] fix: ruff lint and format errors (#7868)

* fix: lint errors

* fix: file formatting

* fix: code refactor
This commit is contained in:
sriram veeraghanta 2025-09-29 19:15:32 +05:30 committed by GitHub
parent 1fb22bd252
commit 9237f568dd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
261 changed files with 2199 additions and 6378 deletions

View file

@ -103,9 +103,7 @@ class TestLabelListCreateAPIEndpoint:
assert created_label.external_source == "github"
@pytest.mark.django_db
def test_create_label_duplicate_external_id(
self, api_key_client, workspace, project
):
def test_create_label_duplicate_external_id(self, api_key_client, workspace, project):
"""Test creating label with duplicate external ID"""
url = self.get_label_url(workspace.slug, project.id)
@ -131,19 +129,13 @@ class TestLabelListCreateAPIEndpoint:
assert "same external id" in response.data["error"]
@pytest.mark.django_db
def test_list_labels_success(
self, api_key_client, workspace, project, create_label
):
def test_list_labels_success(self, api_key_client, workspace, project, create_label):
"""Test successful label listing"""
url = self.get_label_url(workspace.slug, project.id)
# Create additional labels
Label.objects.create(
name="Label 2", project=project, workspace=workspace, color="#00FF00"
)
Label.objects.create(
name="Label 3", project=project, workspace=workspace, color="#0000FF"
)
Label.objects.create(name="Label 2", project=project, workspace=workspace, color="#00FF00")
Label.objects.create(name="Label 3", project=project, workspace=workspace, color="#0000FF")
response = api_key_client.get(url)
@ -184,9 +176,7 @@ class TestLabelDetailAPIEndpoint:
assert response.status_code == status.HTTP_404_NOT_FOUND
@pytest.mark.django_db
def test_update_label_success(
self, api_key_client, workspace, project, create_label
):
def test_update_label_success(self, api_key_client, workspace, project, create_label):
"""Test successful label update"""
url = self.get_label_detail_url(workspace.slug, project.id, create_label.id)
@ -202,9 +192,7 @@ class TestLabelDetailAPIEndpoint:
assert create_label.name == update_data["name"]
@pytest.mark.django_db
def test_update_label_invalid_data(
self, api_key_client, workspace, project, create_label
):
def test_update_label_invalid_data(self, api_key_client, workspace, project, create_label):
"""Test label update with invalid data"""
url = self.get_label_detail_url(workspace.slug, project.id, create_label.id)
@ -215,9 +203,7 @@ class TestLabelDetailAPIEndpoint:
assert response.status_code in [status.HTTP_400_BAD_REQUEST, status.HTTP_200_OK]
@pytest.mark.django_db
def test_delete_label_success(
self, api_key_client, workspace, project, create_label
):
def test_delete_label_success(self, api_key_client, workspace, project, create_label):
"""Test successful label deletion"""
url = self.get_label_detail_url(workspace.slug, project.id, create_label.id)

View file

@ -14,9 +14,7 @@ class TestApiTokenEndpoint:
# POST /user/api-tokens/ tests
@pytest.mark.django_db
def test_create_api_token_success(
self, session_client, create_user, api_token_data
):
def test_create_api_token_success(self, session_client, create_user, api_token_data):
"""Test successful API token creation"""
# Arrange
session_client.force_authenticate(user=create_user)
@ -38,9 +36,7 @@ class TestApiTokenEndpoint:
assert token.label == api_token_data["label"]
@pytest.mark.django_db
def test_create_api_token_for_bot_user(
self, session_client, create_bot_user, api_token_data
):
def test_create_api_token_for_bot_user(self, session_client, create_bot_user, api_token_data):
"""Test API token creation for bot user"""
# Arrange
session_client.force_authenticate(user=create_bot_user)
@ -111,9 +107,7 @@ class TestApiTokenEndpoint:
APIToken.objects.create(label="Token 1", user=create_user, user_type=0)
APIToken.objects.create(label="Token 2", user=create_user, user_type=0)
# Create a service token (should be excluded)
APIToken.objects.create(
label="Service Token", user=create_user, user_type=0, is_service=True
)
APIToken.objects.create(label="Service Token", user=create_user, user_type=0, is_service=True)
url = reverse("api-tokens")
# Act
@ -140,9 +134,7 @@ class TestApiTokenEndpoint:
# GET /user/api-tokens/<pk>/ tests
@pytest.mark.django_db
def test_get_specific_api_token(
self, session_client, create_user, create_api_token_for_user
):
def test_get_specific_api_token(self, session_client, create_user, create_api_token_for_user):
"""Test retrieving a specific API token"""
# Arrange
session_client.force_authenticate(user=create_user)
@ -155,9 +147,7 @@ class TestApiTokenEndpoint:
assert response.status_code == status.HTTP_200_OK
assert str(response.data["id"]) == str(create_api_token_for_user.pk)
assert response.data["label"] == create_api_token_for_user.label
assert (
"token" not in response.data
) # Token should not be visible in read serializer
assert "token" not in response.data # Token should not be visible in read serializer
@pytest.mark.django_db
def test_get_nonexistent_api_token(self, session_client, create_user):
@ -182,9 +172,7 @@ class TestApiTokenEndpoint:
unique_email = f"other-{unique_id}@plane.so"
unique_username = f"other_user_{unique_id}"
other_user = User.objects.create(email=unique_email, username=unique_username)
other_token = APIToken.objects.create(
label="Other Token", user=other_user, user_type=0
)
other_token = APIToken.objects.create(label="Other Token", user=other_user, user_type=0)
session_client.force_authenticate(user=create_user)
url = reverse("api-tokens", kwargs={"pk": other_token.pk})
@ -196,9 +184,7 @@ class TestApiTokenEndpoint:
# DELETE /user/api-tokens/<pk>/ tests
@pytest.mark.django_db
def test_delete_api_token_success(
self, session_client, create_user, create_api_token_for_user
):
def test_delete_api_token_success(self, session_client, create_user, create_api_token_for_user):
"""Test successful API token deletion"""
# Arrange
session_client.force_authenticate(user=create_user)
@ -234,9 +220,7 @@ class TestApiTokenEndpoint:
unique_email = f"delete-other-{unique_id}@plane.so"
unique_username = f"delete_other_user_{unique_id}"
other_user = User.objects.create(email=unique_email, username=unique_username)
other_token = APIToken.objects.create(
label="Other Token", user=other_user, user_type=0
)
other_token = APIToken.objects.create(label="Other Token", user=other_user, user_type=0)
session_client.force_authenticate(user=create_user)
url = reverse("api-tokens", kwargs={"pk": other_token.pk})
@ -252,9 +236,7 @@ class TestApiTokenEndpoint:
def test_delete_service_api_token_forbidden(self, session_client, create_user):
"""Test deleting a service API token (should fail)"""
# Arrange
service_token = APIToken.objects.create(
label="Service Token", user=create_user, user_type=0, is_service=True
)
service_token = APIToken.objects.create(label="Service Token", user=create_user, user_type=0, is_service=True)
session_client.force_authenticate(user=create_user)
url = reverse("api-tokens", kwargs={"pk": service_token.pk})
@ -268,9 +250,7 @@ class TestApiTokenEndpoint:
# PATCH /user/api-tokens/<pk>/ tests
@pytest.mark.django_db
def test_patch_api_token_success(
self, session_client, create_user, create_api_token_for_user
):
def test_patch_api_token_success(self, session_client, create_user, create_api_token_for_user):
"""Test successful API token update"""
# Arrange
session_client.force_authenticate(user=create_user)
@ -294,9 +274,7 @@ class TestApiTokenEndpoint:
assert create_api_token_for_user.description == update_data["description"]
@pytest.mark.django_db
def test_patch_api_token_partial_update(
self, session_client, create_user, create_api_token_for_user
):
def test_patch_api_token_partial_update(self, session_client, create_user, create_api_token_for_user):
"""Test partial API token update"""
# Arrange
session_client.force_authenticate(user=create_user)
@ -336,9 +314,7 @@ class TestApiTokenEndpoint:
unique_email = f"patch-other-{unique_id}@plane.so"
unique_username = f"patch_other_user_{unique_id}"
other_user = User.objects.create(email=unique_email, username=unique_username)
other_token = APIToken.objects.create(
label="Other Token", user=other_user, user_type=0
)
other_token = APIToken.objects.create(label="Other Token", user=other_user, user_type=0)
session_client.force_authenticate(user=create_user)
url = reverse("api-tokens", kwargs={"pk": other_token.pk})
update_data = {"label": "Hacked Label"}

View file

@ -16,9 +16,7 @@ from plane.license.models import Instance
@pytest.fixture
def setup_instance(db):
"""Create and configure an instance for authentication tests"""
instance_id = (
uuid.uuid4() if not Instance.objects.exists() else Instance.objects.first().id
)
instance_id = uuid.uuid4() if not Instance.objects.exists() else Instance.objects.first().id
# Create or update instance with all required fields
instance, _ = Instance.objects.update_or_create(
@ -38,9 +36,7 @@ def setup_instance(db):
@pytest.fixture
def django_client():
"""Return a Django test client with User-Agent header for handling redirects"""
client = Client(
HTTP_USER_AGENT="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1"
)
client = Client(HTTP_USER_AGENT="Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:15.0) Gecko/20100101 Firefox/15.0.1")
return client
@ -83,9 +79,7 @@ class TestMagicLinkGenerate:
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_magic_generate(
self, mock_magic_link, api_client, setup_user, setup_instance
):
def test_magic_generate(self, mock_magic_link, api_client, setup_user, setup_instance):
"""Test successful magic link generation"""
url = reverse("magic-generate")
@ -103,9 +97,7 @@ class TestMagicLinkGenerate:
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_max_generate_attempt(
self, mock_magic_link, api_client, setup_user, setup_instance
):
def test_max_generate_attempt(self, mock_magic_link, api_client, setup_user, setup_instance):
"""Test exceeding maximum magic link generation attempts"""
url = reverse("magic-generate")
@ -145,9 +137,7 @@ class TestSignInEndpoint:
def test_email_validity(self, django_client, setup_user, setup_instance):
"""Test sign-in with invalid email format"""
url = reverse("sign-in")
response = django_client.post(
url, {"email": "useremail.com", "password": "user@123"}, follow=True
)
response = django_client.post(url, {"email": "useremail.com", "password": "user@123"}, follow=True)
# Check redirect contains error code
assert "INVALID_EMAIL_SIGN_IN" in response.redirect_chain[-1][0]
@ -156,9 +146,7 @@ class TestSignInEndpoint:
def test_user_exists(self, django_client, setup_user, setup_instance):
"""Test sign-in with non-existent user"""
url = reverse("sign-in")
response = django_client.post(
url, {"email": "user@email.so", "password": "user123"}, follow=True
)
response = django_client.post(url, {"email": "user@email.so", "password": "user123"}, follow=True)
# Check redirect contains error code
assert "USER_DOES_NOT_EXIST" in response.redirect_chain[-1][0]
@ -167,9 +155,7 @@ class TestSignInEndpoint:
def test_password_validity(self, django_client, setup_user, setup_instance):
"""Test sign-in with incorrect password"""
url = reverse("sign-in")
response = django_client.post(
url, {"email": "user@plane.so", "password": "user123"}, follow=True
)
response = django_client.post(url, {"email": "user@plane.so", "password": "user123"}, follow=True)
# Check for the specific authentication error in the URL
redirect_urls = [url for url, _ in response.redirect_chain]
@ -184,9 +170,7 @@ class TestSignInEndpoint:
url = reverse("sign-in")
# First make the request without following redirects
response = django_client.post(
url, {"email": "user@plane.so", "password": "user@123"}, follow=False
)
response = django_client.post(url, {"email": "user@plane.so", "password": "user@123"}, follow=False)
# Check that the initial response is a redirect (302) without error code
assert response.status_code == 302
@ -243,27 +227,20 @@ class TestMagicSignIn:
assert "MAGIC_SIGN_IN_EMAIL_CODE_REQUIRED" in response.redirect_chain[-1][0]
@pytest.mark.django_db
def test_expired_invalid_magic_link(
self, django_client, setup_user, setup_instance
):
def test_expired_invalid_magic_link(self, django_client, setup_user, setup_instance):
"""Test magic link sign-in with expired/invalid link"""
ri = redis_instance()
ri.delete("magic_user@plane.so")
url = reverse("magic-sign-in")
response = django_client.post(
url, {"email": "user@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False
)
response = django_client.post(url, {"email": "user@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False)
# Check that we get a redirect
assert response.status_code == 302
# The actual error code is EXPIRED_MAGIC_CODE_SIGN_IN (when key doesn't exist)
# or INVALID_MAGIC_CODE_SIGN_IN (when key exists but code doesn't match)
assert (
"EXPIRED_MAGIC_CODE_SIGN_IN" in response.url
or "INVALID_MAGIC_CODE_SIGN_IN" in response.url
)
assert "EXPIRED_MAGIC_CODE_SIGN_IN" in response.url or "INVALID_MAGIC_CODE_SIGN_IN" in response.url
@pytest.mark.django_db
def test_user_does_not_exist(self, django_client, setup_instance):
@ -280,9 +257,7 @@ class TestMagicSignIn:
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_magic_code_sign_in(
self, mock_magic_link, django_client, api_client, setup_user, setup_instance
):
def test_magic_code_sign_in(self, mock_magic_link, django_client, api_client, setup_user, setup_instance):
"""Test successful magic link sign-in process"""
# First generate a magic link token
gen_url = reverse("magic-generate")
@ -298,9 +273,7 @@ class TestMagicSignIn:
# Use Django client to test the redirect flow without following redirects
url = reverse("magic-sign-in")
response = django_client.post(
url, {"email": "user@plane.so", "code": token}, follow=False
)
response = django_client.post(url, {"email": "user@plane.so", "code": token}, follow=False)
# Check that the initial response is a redirect without error code
assert response.status_code == 302
@ -311,9 +284,7 @@ class TestMagicSignIn:
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_magic_sign_in_with_next_path(
self, mock_magic_link, django_client, api_client, setup_user, setup_instance
):
def test_magic_sign_in_with_next_path(self, mock_magic_link, django_client, api_client, setup_user, setup_instance):
"""Test magic sign-in with next_path parameter"""
# First generate a magic link token
gen_url = reverse("magic-generate")
@ -367,9 +338,7 @@ class TestMagicSignUp:
User.objects.create(email="existing@plane.so")
url = reverse("magic-sign-up")
response = django_client.post(
url, {"email": "existing@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=True
)
response = django_client.post(url, {"email": "existing@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=True)
# Check redirect contains error code
assert "USER_ALREADY_EXIST" in response.redirect_chain[-1][0]
@ -378,25 +347,18 @@ class TestMagicSignUp:
def test_expired_invalid_magic_link(self, django_client, setup_instance):
"""Test magic link sign-up with expired/invalid link"""
url = reverse("magic-sign-up")
response = django_client.post(
url, {"email": "new@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False
)
response = django_client.post(url, {"email": "new@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False)
# Check that we get a redirect
assert response.status_code == 302
# The actual error code is EXPIRED_MAGIC_CODE_SIGN_UP (when key doesn't exist)
# or INVALID_MAGIC_CODE_SIGN_UP (when key exists but code doesn't match)
assert (
"EXPIRED_MAGIC_CODE_SIGN_UP" in response.url
or "INVALID_MAGIC_CODE_SIGN_UP" in response.url
)
assert "EXPIRED_MAGIC_CODE_SIGN_UP" in response.url or "INVALID_MAGIC_CODE_SIGN_UP" in response.url
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_magic_code_sign_up(
self, mock_magic_link, django_client, api_client, setup_instance
):
def test_magic_code_sign_up(self, mock_magic_link, django_client, api_client, setup_instance):
"""Test successful magic link sign-up process"""
email = "newuser@plane.so"
@ -414,9 +376,7 @@ class TestMagicSignUp:
# Use Django client to test the redirect flow without following redirects
url = reverse("magic-sign-up")
response = django_client.post(
url, {"email": email, "code": token}, follow=False
)
response = django_client.post(url, {"email": email, "code": token}, follow=False)
# Check that the initial response is a redirect without error code
assert response.status_code == 302
@ -430,9 +390,7 @@ class TestMagicSignUp:
@pytest.mark.django_db
@patch("plane.bgtasks.magic_link_code_task.magic_link.delay")
def test_magic_sign_up_with_next_path(
self, mock_magic_link, django_client, api_client, setup_instance
):
def test_magic_sign_up_with_next_path(self, mock_magic_link, django_client, api_client, setup_instance):
"""Test magic sign-up with next_path parameter"""
email = "newuser2@plane.so"
@ -451,9 +409,7 @@ class TestMagicSignUp:
# Use Django client to test the redirect flow without following redirects
url = reverse("magic-sign-up")
next_path = "onboarding"
response = django_client.post(
url, {"email": email, "code": token, "next_path": next_path}, follow=False
)
response = django_client.post(url, {"email": email, "code": token, "next_path": next_path}, follow=False)
# Check that the initial response is a redirect without error code
assert response.status_code == 302

View file

@ -14,9 +14,7 @@ from plane.db.models import (
class TestProjectBase:
def get_project_url(
self, workspace_slug: str, pk: uuid.UUID = None, details: bool = False
) -> str:
def get_project_url(self, workspace_slug: str, pk: uuid.UUID = None, details: bool = False) -> str:
"""
Constructs the project endpoint URL for the given workspace as reverse() is
unreliable due to duplicate 'name' values in URL patterns ('api' and 'app').
@ -80,9 +78,7 @@ class TestProjectAPIPost(TestProjectBase):
# Check if the member is created with the correct role
assert ProjectMember.objects.count() == 1
project_member = ProjectMember.objects.filter(
project=project, member=user
).first()
project_member = ProjectMember.objects.filter(project=project, member=user).first()
assert project_member.role == 20 # Administrator
assert project_member.is_active is True
@ -97,19 +93,13 @@ class TestProjectAPIPost(TestProjectBase):
assert set(state_names) == set(expected_states)
@pytest.mark.django_db
def test_create_project_with_project_lead(
self, session_client, workspace, create_user
):
def test_create_project_with_project_lead(self, session_client, workspace, create_user):
"""Test creating project with a different project lead"""
# Create another user to be project lead
project_lead = User.objects.create_user(
email="lead@example.com", username="projectlead"
)
project_lead = User.objects.create_user(email="lead@example.com", username="projectlead")
# Add project lead to workspace
WorkspaceMember.objects.create(
workspace=workspace, member=project_lead, role=15
)
WorkspaceMember.objects.create(workspace=workspace, member=project_lead, role=15)
url = self.get_project_url(workspace.slug)
project_data = {
@ -132,9 +122,7 @@ class TestProjectAPIPost(TestProjectBase):
@pytest.mark.django_db
def test_create_project_guest_forbidden(self, session_client, workspace):
"""Test that guests cannot create projects"""
guest_user = User.objects.create_user(
email="guest@example.com", username="guest"
)
guest_user = User.objects.create_user(email="guest@example.com", username="guest")
WorkspaceMember.objects.create(workspace=workspace, member=guest_user, role=5)
session_client.force_authenticate(user=guest_user)
@ -164,14 +152,10 @@ class TestProjectAPIPost(TestProjectBase):
assert response.status_code == status.HTTP_401_UNAUTHORIZED
@pytest.mark.django_db
def test_create_project_duplicate_name(
self, session_client, workspace, create_user
):
def test_create_project_duplicate_name(self, session_client, workspace, create_user):
"""Test creating project with duplicate name"""
# Create first project
Project.objects.create(
name="Duplicate Name", identifier="DN1", workspace=workspace
)
Project.objects.create(name="Duplicate Name", identifier="DN1", workspace=workspace)
url = self.get_project_url(workspace.slug)
project_data = {
@ -184,13 +168,9 @@ class TestProjectAPIPost(TestProjectBase):
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_create_project_duplicate_identifier(
self, session_client, workspace, create_user
):
def test_create_project_duplicate_identifier(self, session_client, workspace, create_user):
"""Test creating project with duplicate identifier"""
Project.objects.create(
name="First Project", identifier="DUP", workspace=workspace
)
Project.objects.create(name="First Project", identifier="DUP", workspace=workspace)
url = self.get_project_url(workspace.slug)
project_data = {
@ -203,9 +183,7 @@ class TestProjectAPIPost(TestProjectBase):
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_create_project_missing_required_fields(
self, session_client, workspace, create_user
):
def test_create_project_missing_required_fields(self, session_client, workspace, create_user):
"""Test validation with missing required fields"""
url = self.get_project_url(workspace.slug)
@ -214,15 +192,11 @@ class TestProjectAPIPost(TestProjectBase):
assert response.status_code == status.HTTP_400_BAD_REQUEST
# Test missing identifier
response = session_client.post(
url, {"name": "Missing Identifier"}, format="json"
)
response = session_client.post(url, {"name": "Missing Identifier"}, format="json")
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_create_project_with_all_optional_fields(
self, session_client, workspace, create_user
):
def test_create_project_with_all_optional_fields(self, session_client, workspace, create_user):
"""Test creating project with all optional fields"""
url = self.get_project_url(workspace.slug)
project_data = {
@ -256,19 +230,13 @@ class TestProjectAPIGet(TestProjectBase):
"""Test project GET operations"""
@pytest.mark.django_db
def test_list_projects_authenticated_admin(
self, session_client, workspace, create_user
):
def test_list_projects_authenticated_admin(self, session_client, workspace, create_user):
"""Test listing projects as workspace admin"""
# Create a project
project = Project.objects.create(
name="Test Project", identifier="TP", workspace=workspace
)
project = Project.objects.create(name="Test Project", identifier="TP", workspace=workspace)
# Add user as project member
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug)
response = session_client.get(url)
@ -283,24 +251,16 @@ class TestProjectAPIGet(TestProjectBase):
def test_list_projects_authenticated_guest(self, session_client, workspace):
"""Test listing projects as workspace guest"""
# Create a guest user
guest_user = User.objects.create_user(
email="guest@example.com", username="guest"
)
WorkspaceMember.objects.create(
workspace=workspace, member=guest_user, role=5, is_active=True
)
guest_user = User.objects.create_user(email="guest@example.com", username="guest")
WorkspaceMember.objects.create(workspace=workspace, member=guest_user, role=5, is_active=True)
# Create projects
project1 = Project.objects.create(
name="Project 1", identifier="P1", workspace=workspace
)
project1 = Project.objects.create(name="Project 1", identifier="P1", workspace=workspace)
Project.objects.create(name="Project 2", identifier="P2", workspace=workspace)
# Add guest to only one project
ProjectMember.objects.create(
project=project1, member=guest_user, role=10, is_active=True
)
ProjectMember.objects.create(project=project1, member=guest_user, role=10, is_active=True)
session_client.force_authenticate(user=guest_user)
@ -333,9 +293,7 @@ class TestProjectAPIGet(TestProjectBase):
)
# Add user as project member
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, details=True)
response = session_client.get(url)
@ -358,9 +316,7 @@ class TestProjectAPIGet(TestProjectBase):
)
# Add user as project member
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project.id)
response = session_client.get(url)
@ -392,9 +348,7 @@ class TestProjectAPIGet(TestProjectBase):
)
# Add user as project member
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project.id)
response = session_client.get(url)
@ -407,9 +361,7 @@ class TestProjectAPIPatchDelete(TestProjectBase):
"""Test project PATCH, and DELETE operations"""
@pytest.mark.django_db
def test_partial_update_project_success(
self, session_client, workspace, create_user
):
def test_partial_update_project_success(self, session_client, workspace, create_user):
"""Test successful partial update of project"""
# Create a project
project = Project.objects.create(
@ -420,9 +372,7 @@ class TestProjectAPIPatchDelete(TestProjectBase):
)
# Add user as project administrator
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project.id)
update_data = {
@ -444,25 +394,15 @@ class TestProjectAPIPatchDelete(TestProjectBase):
assert project.module_view is False
@pytest.mark.django_db
def test_partial_update_project_forbidden_non_admin(
self, session_client, workspace
):
def test_partial_update_project_forbidden_non_admin(self, session_client, workspace):
"""Test that non-admin project members cannot update project"""
# Create a project
project = Project.objects.create(
name="Protected Project", identifier="PP", workspace=workspace
)
project = Project.objects.create(name="Protected Project", identifier="PP", workspace=workspace)
# Create a member user (not admin)
member_user = User.objects.create_user(
email="member@example.com", username="member"
)
WorkspaceMember.objects.create(
workspace=workspace, member=member_user, role=15, is_active=True
)
ProjectMember.objects.create(
project=project, member=member_user, role=15, is_active=True
)
member_user = User.objects.create_user(email="member@example.com", username="member")
WorkspaceMember.objects.create(workspace=workspace, member=member_user, role=15, is_active=True)
ProjectMember.objects.create(project=project, member=member_user, role=15, is_active=True)
session_client.force_authenticate(user=member_user)
@ -474,19 +414,13 @@ class TestProjectAPIPatchDelete(TestProjectBase):
assert response.status_code == status.HTTP_403_FORBIDDEN
@pytest.mark.django_db
def test_partial_update_duplicate_name_conflict(
self, session_client, workspace, create_user
):
def test_partial_update_duplicate_name_conflict(self, session_client, workspace, create_user):
"""Test updating project with duplicate name returns conflict"""
# Create two projects
Project.objects.create(name="Project One", identifier="P1", workspace=workspace)
project2 = Project.objects.create(
name="Project Two", identifier="P2", workspace=workspace
)
project2 = Project.objects.create(name="Project Two", identifier="P2", workspace=workspace)
ProjectMember.objects.create(
project=project2, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project2, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project2.id)
update_data = {"name": "Project One"} # Duplicate name
@ -496,19 +430,13 @@ class TestProjectAPIPatchDelete(TestProjectBase):
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_partial_update_duplicate_identifier_conflict(
self, session_client, workspace, create_user
):
def test_partial_update_duplicate_identifier_conflict(self, session_client, workspace, create_user):
"""Test updating project with duplicate identifier returns conflict"""
# Create two projects
Project.objects.create(name="Project One", identifier="P1", workspace=workspace)
project2 = Project.objects.create(
name="Project Two", identifier="P2", workspace=workspace
)
project2 = Project.objects.create(name="Project Two", identifier="P2", workspace=workspace)
ProjectMember.objects.create(
project=project2, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project2, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project2.id)
update_data = {"identifier": "P1"} # Duplicate identifier
@ -520,13 +448,9 @@ class TestProjectAPIPatchDelete(TestProjectBase):
@pytest.mark.django_db
def test_partial_update_invalid_data(self, session_client, workspace, create_user):
"""Test partial update with invalid data"""
project = Project.objects.create(
name="Valid Project", identifier="VP", workspace=workspace
)
project = Project.objects.create(name="Valid Project", identifier="VP", workspace=workspace)
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project.id)
update_data = {"name": ""}
@ -536,17 +460,11 @@ class TestProjectAPIPatchDelete(TestProjectBase):
assert response.status_code == status.HTTP_400_BAD_REQUEST
@pytest.mark.django_db
def test_delete_project_success_project_admin(
self, session_client, workspace, create_user
):
def test_delete_project_success_project_admin(self, session_client, workspace, create_user):
"""Test successful project deletion by project admin"""
project = Project.objects.create(
name="Delete Me", identifier="DM", workspace=workspace
)
project = Project.objects.create(name="Delete Me", identifier="DM", workspace=workspace)
ProjectMember.objects.create(
project=project, member=create_user, role=20, is_active=True
)
ProjectMember.objects.create(project=project, member=create_user, role=20, is_active=True)
url = self.get_project_url(workspace.slug, pk=project.id)
response = session_client.delete(url)
@ -558,16 +476,10 @@ class TestProjectAPIPatchDelete(TestProjectBase):
def test_delete_project_success_workspace_admin(self, session_client, workspace):
"""Test successful project deletion by workspace admin"""
# Create workspace admin user
workspace_admin = User.objects.create_user(
email="admin@example.com", username="admin"
)
WorkspaceMember.objects.create(
workspace=workspace, member=workspace_admin, role=20, is_active=True
)
workspace_admin = User.objects.create_user(email="admin@example.com", username="admin")
WorkspaceMember.objects.create(workspace=workspace, member=workspace_admin, role=20, is_active=True)
project = Project.objects.create(
name="Delete Me", identifier="DM", workspace=workspace
)
project = Project.objects.create(name="Delete Me", identifier="DM", workspace=workspace)
session_client.force_authenticate(user=workspace_admin)
@ -581,20 +493,12 @@ class TestProjectAPIPatchDelete(TestProjectBase):
def test_delete_project_forbidden_non_admin(self, session_client, workspace):
"""Test that non-admin users cannot delete projects"""
# Create a member user (not admin)
member_user = User.objects.create_user(
email="member@example.com", username="member"
)
WorkspaceMember.objects.create(
workspace=workspace, member=member_user, role=15, is_active=True
)
member_user = User.objects.create_user(email="member@example.com", username="member")
WorkspaceMember.objects.create(workspace=workspace, member=member_user, role=15, is_active=True)
project = Project.objects.create(
name="Protected Project", identifier="PP", workspace=workspace
)
project = Project.objects.create(name="Protected Project", identifier="PP", workspace=workspace)
ProjectMember.objects.create(
project=project, member=member_user, role=15, is_active=True
)
ProjectMember.objects.create(project=project, member=member_user, role=15, is_active=True)
session_client.force_authenticate(user=member_user)
@ -607,9 +511,7 @@ class TestProjectAPIPatchDelete(TestProjectBase):
@pytest.mark.django_db
def test_delete_project_unauthenticated(self, client, workspace):
"""Test unauthenticated project deletion"""
project = Project.objects.create(
name="Protected Project", identifier="PP", workspace=workspace
)
project = Project.objects.create(name="Protected Project", identifier="PP", workspace=workspace)
url = self.get_project_url(workspace.slug, pk=project.id)
response = client.delete(url)

View file

@ -21,9 +21,7 @@ class TestWorkspaceAPI:
@pytest.mark.django_db
@patch("plane.bgtasks.workspace_seed_task.workspace_seed.delay")
def test_create_workspace_valid_data(
self, mock_workspace_seed, session_client, create_user
):
def test_create_workspace_valid_data(self, mock_workspace_seed, session_client, create_user):
"""Test creating a workspace with valid data"""
url = reverse("workspace")
user = create_user # Use the create_user fixture directly as it returns a user object
@ -49,9 +47,7 @@ class TestWorkspaceAPI:
# Check other values
workspace = Workspace.objects.get(slug=workspace_data["slug"])
workspace_member = WorkspaceMember.objects.filter(
workspace=workspace, member=user
).first()
workspace_member = WorkspaceMember.objects.filter(workspace=workspace, member=user).first()
assert workspace.owner == user
assert workspace_member.role == 20
@ -68,9 +64,7 @@ class TestWorkspaceAPI:
session_client.post(url, {"name": "Plane", "slug": "pla-ne"}, format="json")
# Try to create a workspace with the same slug
response = session_client.post(
url, {"name": "Plane", "slug": "pla-ne"}, format="json"
)
response = session_client.post(url, {"name": "Plane", "slug": "pla-ne"}, format="json")
# The API returns 400 BAD REQUEST for duplicate slugs, not 409 CONFLICT
assert response.status_code == status.HTTP_400_BAD_REQUEST