chore: format files using prettier (#7364)

* chore: format files using prettier

* chore: api server files formatted
This commit is contained in:
sriram veeraghanta 2025-07-08 20:41:11 +05:30 committed by GitHub
parent 0225d806cc
commit 6ce700fd5d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
149 changed files with 1518 additions and 919 deletions

View file

@ -60,6 +60,7 @@ from plane.utils.host import base_host
from plane.bgtasks.webhook_task import model_activity
from plane.bgtasks.work_item_link_task import crawl_work_item_link_title
class WorkspaceIssueAPIEndpoint(BaseAPIView):
"""
This viewset provides `retrieveByIssueId` on workspace level

View file

@ -102,4 +102,4 @@ class CycleUserPropertiesSerializer(BaseSerializer):
class Meta:
model = CycleUserProperties
fields = "__all__"
read_only_fields = ["workspace", "project", "cycle" "user"]
read_only_fields = ["workspace", "project", "cycle", "user"]

View file

@ -726,7 +726,6 @@ class IssueSerializer(DynamicBaseSerializer):
class IssueListDetailSerializer(serializers.Serializer):
def __init__(self, *args, **kwargs):
# Extract expand parameter and store it as instance variable
self.expand = kwargs.pop("expand", []) or []

View file

@ -148,8 +148,8 @@ class ProjectMemberAdminSerializer(BaseSerializer):
fields = "__all__"
class ProjectMemberRoleSerializer(DynamicBaseSerializer):
original_role = serializers.IntegerField(source='role', read_only=True)
class ProjectMemberRoleSerializer(DynamicBaseSerializer):
original_role = serializers.IntegerField(source="role", read_only=True)
class Meta:
model = ProjectMember

View file

@ -110,7 +110,11 @@ class UserMeSettingsSerializer(BaseSerializer):
workspace_member__member=obj.id,
workspace_member__is_active=True,
).first()
logo_asset_url = workspace.logo_asset.asset_url if workspace.logo_asset is not None else ""
logo_asset_url = (
workspace.logo_asset.asset_url
if workspace.logo_asset is not None
else ""
)
return {
"last_workspace_id": profile.last_workspace_id,
"last_workspace_slug": (

View file

@ -8,7 +8,6 @@ from plane.utils.issue_filters import issue_filters
class ViewIssueListSerializer(serializers.Serializer):
def get_assignee_ids(self, instance):
return [assignee.assignee_id for assignee in instance.issue_assignee.all()]

View file

@ -160,7 +160,8 @@ class AdvanceAnalyticsStatsEndpoint(AdvanceAnalyticsBaseView):
)
return (
base_queryset.values("project_id", "project__name").annotate(
base_queryset.values("project_id", "project__name")
.annotate(
cancelled_work_items=Count("id", filter=Q(state__group="cancelled")),
completed_work_items=Count("id", filter=Q(state__group="completed")),
backlog_work_items=Count("id", filter=Q(state__group="backlog")),
@ -173,8 +174,7 @@ class AdvanceAnalyticsStatsEndpoint(AdvanceAnalyticsBaseView):
def get_work_items_stats(self) -> Dict[str, Dict[str, int]]:
base_queryset = Issue.issue_objects.filter(**self.filters["base_filters"])
return (
base_queryset
.values("project_id", "project__name")
base_queryset.values("project_id", "project__name")
.annotate(
cancelled_work_items=Count("id", filter=Q(state__group="cancelled")),
completed_work_items=Count("id", filter=Q(state__group="completed")),

View file

@ -37,7 +37,7 @@ class NotificationViewSet(BaseViewSet, BasePaginator):
workspace__slug=self.kwargs.get("slug"),
receiver_id=self.request.user.id,
)
.select_related("workspace", "project," "triggered_by", "receiver")
.select_related("workspace", "project", "triggered_by", "receiver")
)
@allow_permission(

View file

@ -50,7 +50,7 @@ class Command(BaseCommand):
project_count = int(input("Number of projects to be created: "))
for i in range(project_count):
print(f"Please provide the following details for project {i+1}:")
print(f"Please provide the following details for project {i + 1}:")
issue_count = int(input("Number of issues to be created: "))
cycle_count = int(input("Number of cycles to be created: "))
module_count = int(input("Number of modules to be created: "))

View file

@ -134,7 +134,7 @@ def workspace(create_user):
)
WorkspaceMember.objects.create(
workspace=created_workspace, member=create_user, role=20
)
workspace=created_workspace, member=create_user, role=20
)
return created_workspace

View file

@ -21,7 +21,7 @@ def mock_redis():
mock_redis_client.ttl.return_value = -1
# Start the patch
with patch('plane.settings.redis.redis_instance', return_value=mock_redis_client):
with patch("plane.settings.redis.redis_instance", return_value=mock_redis_client):
yield mock_redis_client
@ -44,7 +44,7 @@ def mock_elasticsearch():
mock_es_client.delete.return_value = {"_id": "test_id", "result": "deleted"}
# Start the patch
with patch('elasticsearch.Elasticsearch', return_value=mock_es_client):
with patch("elasticsearch.Elasticsearch", return_value=mock_es_client):
yield mock_es_client
@ -68,39 +68,30 @@ def mock_mongodb():
# Configure common MongoDB collection operations
mock_mongo_collection.find_one.return_value = None
mock_mongo_collection.find.return_value = MagicMock(
__iter__=lambda x: iter([]),
count=lambda: 0
__iter__=lambda x: iter([]), count=lambda: 0
)
mock_mongo_collection.insert_one.return_value = MagicMock(
inserted_id="mock_id_123",
acknowledged=True
inserted_id="mock_id_123", acknowledged=True
)
mock_mongo_collection.insert_many.return_value = MagicMock(
inserted_ids=["mock_id_123", "mock_id_456"],
acknowledged=True
inserted_ids=["mock_id_123", "mock_id_456"], acknowledged=True
)
mock_mongo_collection.update_one.return_value = MagicMock(
modified_count=1,
matched_count=1,
acknowledged=True
modified_count=1, matched_count=1, acknowledged=True
)
mock_mongo_collection.update_many.return_value = MagicMock(
modified_count=2,
matched_count=2,
acknowledged=True
modified_count=2, matched_count=2, acknowledged=True
)
mock_mongo_collection.delete_one.return_value = MagicMock(
deleted_count=1,
acknowledged=True
deleted_count=1, acknowledged=True
)
mock_mongo_collection.delete_many.return_value = MagicMock(
deleted_count=2,
acknowledged=True
deleted_count=2, acknowledged=True
)
mock_mongo_collection.count_documents.return_value = 0
# Start the patch
with patch('pymongo.MongoClient', return_value=mock_mongo_client):
with patch("pymongo.MongoClient", return_value=mock_mongo_client):
yield mock_mongo_client
@ -112,6 +103,6 @@ def mock_celery():
This fixture patches Celery's task.delay() to prevent actual task execution.
"""
# Start the patch
with patch('celery.app.task.Task.delay') as mock_delay:
with patch("celery.app.task.Task.delay") as mock_delay:
mock_delay.return_value = MagicMock(id="mock-task-id")
yield mock_delay
yield mock_delay

View file

@ -16,7 +16,9 @@ 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(
@ -28,7 +30,7 @@ def setup_instance(db):
"domain": "http://localhost:8000",
"last_checked_at": timezone.now(),
"is_setup_done": True,
}
},
)
return instance
@ -36,7 +38,9 @@ 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
@ -79,7 +83,9 @@ 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")
@ -97,7 +103,9 @@ 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")
@ -163,10 +171,9 @@ class TestSignInEndpoint:
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]
redirect_contents = ' '.join(redirect_urls)
redirect_contents = " ".join(redirect_urls)
# The actual error code for invalid password is AUTHENTICATION_FAILED_SIGN_IN
assert "AUTHENTICATION_FAILED_SIGN_IN" in redirect_contents
@ -201,14 +208,13 @@ class TestSignInEndpoint:
response = django_client.post(
url,
{"email": "user@plane.so", "password": "user@123", "next_path": next_path},
follow=False
follow=False,
)
# Check that the initial response is a redirect (302) without error code
assert response.status_code == 302
assert "error_code" not in response.url
# In a real browser, the next_path would be used to build the absolute URL
# Since we're just testing the authentication logic, we won't check for the exact URL structure
# Instead, just verify that we're authenticated
@ -237,16 +243,16 @@ 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
url, {"email": "user@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False
)
# Check that we get a redirect
@ -254,7 +260,10 @@ class TestMagicSignIn:
# 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):
@ -263,7 +272,7 @@ class TestMagicSignIn:
response = django_client.post(
url,
{"email": "nonexistent@plane.so", "code": "xxxx-xxxxx-xxxx"},
follow=True
follow=True,
)
# Check redirect contains error code
@ -271,7 +280,9 @@ 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")
@ -288,9 +299,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
url, {"email": "user@plane.so", "code": token}, follow=False
)
# Check that the initial response is a redirect without error code
@ -302,7 +311,9 @@ 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")
@ -322,7 +333,7 @@ class TestMagicSignIn:
response = django_client.post(
url,
{"email": "user@plane.so", "code": token, "next_path": next_path},
follow=False
follow=False,
)
# Check that the initial response is a redirect without error code
@ -357,9 +368,7 @@ class TestMagicSignUp:
url = reverse("magic-sign-up")
response = django_client.post(
url,
{"email": "existing@plane.so", "code": "xxxx-xxxxx-xxxx"},
follow=True
url, {"email": "existing@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=True
)
# Check redirect contains error code
@ -370,9 +379,7 @@ class TestMagicSignUp:
"""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
url, {"email": "new@plane.so", "code": "xxxx-xxxxx-xxxx"}, follow=False
)
# Check that we get a redirect
@ -380,11 +387,16 @@ class TestMagicSignUp:
# 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"
@ -403,9 +415,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
url, {"email": email, "code": token}, follow=False
)
# Check that the initial response is a redirect without error code
@ -420,7 +430,9 @@ 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"
@ -440,9 +452,7 @@ class TestMagicSignUp:
url = reverse("magic-sign-up")
next_path = "onboarding"
response = django_client.post(
url,
{"email": email, "code": token, "next_path": next_path},
follow=False
url, {"email": email, "code": token, "next_path": next_path}, follow=False
)
# Check that the initial response is a redirect without error code
@ -456,4 +466,4 @@ class TestMagicSignUp:
assert User.objects.filter(email=email).exists()
# Check if user is authenticated
assert "_auth_user_id" in django_client.session
assert "_auth_user_id" in django_client.session

View file

@ -21,7 +21,9 @@ 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
@ -30,7 +32,7 @@ class TestWorkspaceAPI:
workspace_data = {
"name": "Plane",
"slug": "pla-ne-test",
"company_name": "Plane Inc."
"company_name": "Plane Inc.",
}
# Make the request
@ -57,15 +59,13 @@ class TestWorkspaceAPI:
mock_workspace_seed.assert_called_once_with(response.data["id"])
@pytest.mark.django_db
@patch('plane.bgtasks.workspace_seed_task.workspace_seed.delay')
@patch("plane.bgtasks.workspace_seed_task.workspace_seed.delay")
def test_create_duplicate_workspace(self, mock_workspace_seed, session_client):
"""Test creating a duplicate workspace"""
url = reverse("workspace")
# Create first workspace
session_client.post(
url, {"name": "Plane", "slug": "pla-ne"}, format="json"
)
session_client.post(url, {"name": "Plane", "slug": "pla-ne"}, format="json")
# Try to create a workspace with the same slug
response = session_client.post(
@ -76,4 +76,4 @@ class TestWorkspaceAPI:
assert response.status_code == status.HTTP_400_BAD_REQUEST
# Optionally check the error message to confirm it's related to the duplicate slug
assert "slug" in response.data
assert "slug" in response.data

View file

@ -2,26 +2,21 @@ import factory
from uuid import uuid4
from django.utils import timezone
from plane.db.models import (
User,
Workspace,
WorkspaceMember,
Project,
ProjectMember
)
from plane.db.models import User, Workspace, WorkspaceMember, Project, ProjectMember
class UserFactory(factory.django.DjangoModelFactory):
"""Factory for creating User instances"""
class Meta:
model = User
django_get_or_create = ('email',)
django_get_or_create = ("email",)
id = factory.LazyFunction(uuid4)
email = factory.Sequence(lambda n: f'user{n}@plane.so')
password = factory.PostGenerationMethodCall('set_password', 'password')
first_name = factory.Sequence(lambda n: f'First{n}')
last_name = factory.Sequence(lambda n: f'Last{n}')
email = factory.Sequence(lambda n: f"user{n}@plane.so")
password = factory.PostGenerationMethodCall("set_password", "password")
first_name = factory.Sequence(lambda n: f"First{n}")
last_name = factory.Sequence(lambda n: f"Last{n}")
is_active = True
is_superuser = False
is_staff = False
@ -29,13 +24,14 @@ class UserFactory(factory.django.DjangoModelFactory):
class WorkspaceFactory(factory.django.DjangoModelFactory):
"""Factory for creating Workspace instances"""
class Meta:
model = Workspace
django_get_or_create = ('slug',)
django_get_or_create = ("slug",)
id = factory.LazyFunction(uuid4)
name = factory.Sequence(lambda n: f'Workspace {n}')
slug = factory.Sequence(lambda n: f'workspace-{n}')
name = factory.Sequence(lambda n: f"Workspace {n}")
slug = factory.Sequence(lambda n: f"workspace-{n}")
owner = factory.SubFactory(UserFactory)
created_at = factory.LazyFunction(timezone.now)
updated_at = factory.LazyFunction(timezone.now)
@ -43,6 +39,7 @@ class WorkspaceFactory(factory.django.DjangoModelFactory):
class WorkspaceMemberFactory(factory.django.DjangoModelFactory):
"""Factory for creating WorkspaceMember instances"""
class Meta:
model = WorkspaceMember
@ -56,21 +53,23 @@ class WorkspaceMemberFactory(factory.django.DjangoModelFactory):
class ProjectFactory(factory.django.DjangoModelFactory):
"""Factory for creating Project instances"""
class Meta:
model = Project
django_get_or_create = ('name', 'workspace')
django_get_or_create = ("name", "workspace")
id = factory.LazyFunction(uuid4)
name = factory.Sequence(lambda n: f'Project {n}')
name = factory.Sequence(lambda n: f"Project {n}")
workspace = factory.SubFactory(WorkspaceFactory)
created_by = factory.SelfAttribute('workspace.owner')
updated_by = factory.SelfAttribute('workspace.owner')
created_by = factory.SelfAttribute("workspace.owner")
updated_by = factory.SelfAttribute("workspace.owner")
created_at = factory.LazyFunction(timezone.now)
updated_at = factory.LazyFunction(timezone.now)
class ProjectMemberFactory(factory.django.DjangoModelFactory):
"""Factory for creating ProjectMember instances"""
class Meta:
model = ProjectMember
@ -79,4 +78,4 @@ class ProjectMemberFactory(factory.django.DjangoModelFactory):
member = factory.SubFactory(UserFactory)
role = 20 # Admin role by default
created_at = factory.LazyFunction(timezone.now)
updated_at = factory.LazyFunction(timezone.now)
updated_at = factory.LazyFunction(timezone.now)

View file

@ -16,72 +16,79 @@ class TestAuthSmoke:
# 1. Test bad login - test with wrong password
response = requests.post(
url,
data={
"email": user_data["email"],
"password": "wrong-password"
}
url, data={"email": user_data["email"], "password": "wrong-password"}
)
# For bad credentials, any of these status codes would be valid
# The test shouldn't be brittle to minor implementation changes
assert response.status_code != 500, "Authentication should not cause server errors"
assert response.status_code != 500, (
"Authentication should not cause server errors"
)
assert response.status_code != 404, "Authentication endpoint should exist"
if response.status_code == 200:
# If API returns 200 for failures, check the response body for error indication
if hasattr(response, 'json'):
if hasattr(response, "json"):
try:
data = response.json()
# JSON response might indicate error in its structure
assert "error" in data or "error_code" in data or "detail" in data or response.url.endswith("sign-in"), \
"Error response should contain error details"
assert (
"error" in data
or "error_code" in data
or "detail" in data
or response.url.endswith("sign-in")
), "Error response should contain error details"
except ValueError:
# It's ok if response isn't JSON format
pass
elif response.status_code in [302, 303]:
# If it's a redirect, it should redirect to a login page or error page
redirect_url = response.headers.get('Location', '')
assert "error" in redirect_url or "sign-in" in redirect_url, \
redirect_url = response.headers.get("Location", "")
assert "error" in redirect_url or "sign-in" in redirect_url, (
"Failed login should redirect to login page or error page"
)
# 2. Test good login with correct credentials
response = requests.post(
url,
data={
"email": user_data["email"],
"password": user_data["password"]
},
allow_redirects=False # Don't follow redirects
data={"email": user_data["email"], "password": user_data["password"]},
allow_redirects=False, # Don't follow redirects
)
# Successful auth should not be a client error or server error
assert response.status_code not in range(400, 600), \
assert response.status_code not in range(400, 600), (
f"Authentication with valid credentials failed with status {response.status_code}"
)
# Specific validation based on response type
if response.status_code in [302, 303]:
# Redirect-based auth: check that redirect URL doesn't contain error
redirect_url = response.headers.get('Location', '')
assert "error" not in redirect_url and "error_code" not in redirect_url, \
redirect_url = response.headers.get("Location", "")
assert "error" not in redirect_url and "error_code" not in redirect_url, (
"Successful login redirect should not contain error parameters"
)
elif response.status_code == 200:
# API token-based auth: check for tokens or user session
if hasattr(response, 'json'):
if hasattr(response, "json"):
try:
data = response.json()
# If it's a token response
if "access_token" in data:
assert "refresh_token" in data, "JWT auth should return both access and refresh tokens"
assert "refresh_token" in data, (
"JWT auth should return both access and refresh tokens"
)
# If it's a user session response
elif "user" in data:
assert "is_authenticated" in data and data["is_authenticated"], \
"User session response should indicate authentication"
assert (
"is_authenticated" in data and data["is_authenticated"]
), "User session response should indicate authentication"
# Otherwise it should at least indicate success
else:
assert not any(error_key in data for error_key in ["error", "error_code", "detail"]), \
"Success response should not contain error keys"
assert not any(
error_key in data
for error_key in ["error", "error_code", "detail"]
), "Success response should not contain error keys"
except ValueError:
# Non-JSON is acceptable if it's a redirect or HTML response
pass
@ -97,4 +104,4 @@ class TestHealthCheckSmoke:
response = requests.get(f"{plane_server.url}/")
# Should be OK
assert response.status_code == 200, "Health check endpoint should return 200 OK"
assert response.status_code == 200, "Health check endpoint should return 200 OK"

View file

@ -13,10 +13,7 @@ class TestWorkspaceModel:
"""Test creating a workspace"""
# Create a workspace
workspace = Workspace.objects.create(
name="Test Workspace",
slug="test-workspace",
id=uuid4(),
owner=create_user
name="Test Workspace", slug="test-workspace", id=uuid4(), owner=create_user
)
# Verify it was created
@ -30,21 +27,18 @@ class TestWorkspaceModel:
"""Test creating a workspace member"""
# Create a workspace
workspace = Workspace.objects.create(
name="Test Workspace",
slug="test-workspace",
id=uuid4(),
owner=create_user
name="Test Workspace", slug="test-workspace", id=uuid4(), owner=create_user
)
# Create a workspace member
workspace_member = WorkspaceMember.objects.create(
workspace=workspace,
member=create_user,
role=20 # Admin role
role=20, # Admin role
)
# Verify it was created
assert workspace_member.id is not None
assert workspace_member.workspace == workspace
assert workspace_member.member == create_user
assert workspace_member.role == 20
assert workspace_member.role == 20

View file

@ -13,18 +13,13 @@ class TestWorkspaceLiteSerializer:
"""Test that the serializer includes the correct fields"""
# Create a user to be the owner
owner = User.objects.create(
email="test@example.com",
first_name="Test",
last_name="User"
email="test@example.com", first_name="Test", last_name="User"
)
# Create a workspace with explicit ID to test serialization
workspace_id = uuid4()
workspace = Workspace.objects.create(
name="Test Workspace",
slug="test-workspace",
id=workspace_id,
owner=owner
name="Test Workspace", slug="test-workspace", id=workspace_id, owner=owner
)
# Serialize the workspace
@ -43,23 +38,17 @@ class TestWorkspaceLiteSerializer:
"""Test that the serializer fields are read-only"""
# Create a user to be the owner
owner = User.objects.create(
email="test2@example.com",
first_name="Test",
last_name="User"
email="test2@example.com", first_name="Test", last_name="User"
)
# Create a workspace
workspace = Workspace.objects.create(
name="Test Workspace",
slug="test-workspace",
id=uuid4(),
owner=owner
name="Test Workspace", slug="test-workspace", id=uuid4(), owner=owner
)
# Try to update via serializer
serializer = WorkspaceLiteSerializer(
workspace,
data={"name": "Updated Name", "slug": "updated-slug"}
workspace, data={"name": "Updated Name", "slug": "updated-slug"}
)
# Serializer should be valid (since read-only fields are ignored)
@ -68,4 +57,4 @@ class TestWorkspaceLiteSerializer:
# Save should not update the read-only fields
updated_workspace = serializer.save()
assert updated_workspace.name == "Test Workspace"
assert updated_workspace.slug == "test-workspace"
assert updated_workspace.slug == "test-workspace"

View file

@ -19,7 +19,9 @@ class TestUUIDUtils:
assert is_valid_uuid("not-a-uuid") is False
assert is_valid_uuid("123456789") is False
assert is_valid_uuid("") is False
assert is_valid_uuid("00000000-0000-0000-0000-000000000000") is False # This is a valid UUID but version 1
assert (
is_valid_uuid("00000000-0000-0000-0000-000000000000") is False
) # This is a valid UUID but version 1
def test_convert_uuid_to_integer(self):
"""Test convert_uuid_to_integer function"""
@ -46,4 +48,6 @@ class TestUUIDUtils:
test_uuid = uuid.UUID(test_uuid_str)
# Should get the same result whether passing UUID or string
assert convert_uuid_to_integer(test_uuid) == convert_uuid_to_integer(test_uuid_str)
assert convert_uuid_to_integer(test_uuid) == convert_uuid_to_integer(
test_uuid_str
)

View file

@ -50,11 +50,11 @@ def paginate(base_queryset, queryset, cursor, on_result):
paginated_data = queryset[start_index:end_index]
# Create the pagination info object
prev_cursor = f"{page_size}:{cursor_object.current_page-1}:0"
prev_cursor = f"{page_size}:{cursor_object.current_page - 1}:0"
cursor = f"{page_size}:{cursor_object.current_page}:0"
next_cursor = None
if end_index < total_results:
next_cursor = f"{page_size}:{cursor_object.current_page+1}:0"
next_cursor = f"{page_size}:{cursor_object.current_page + 1}:0"
prev_page_results = False
if cursor_object.current_page > 0:

View file

@ -35,7 +35,7 @@ class Cursor:
# Return the representation of the cursor
def __repr__(self):
return f"{type(self).__name__,}: value={self.value} offset={self.offset}, is_prev={int(self.is_prev)}"
return f"{(type(self).__name__,)}: value={self.value} offset={self.offset}, is_prev={int(self.is_prev)}" # noqa: E501
# Return if the cursor is true
def __bool__(self):

View file

@ -6,36 +6,20 @@ import sys
def main():
parser = argparse.ArgumentParser(description="Run Plane tests")
parser.add_argument("-u", "--unit", action="store_true", help="Run unit tests only")
parser.add_argument(
"-u", "--unit",
action="store_true",
help="Run unit tests only"
"-c", "--contract", action="store_true", help="Run contract tests only"
)
parser.add_argument(
"-c", "--contract",
action="store_true",
help="Run contract tests only"
"-s", "--smoke", action="store_true", help="Run smoke tests only"
)
parser.add_argument(
"-s", "--smoke",
action="store_true",
help="Run smoke tests only"
"-o", "--coverage", action="store_true", help="Generate coverage report"
)
parser.add_argument(
"-o", "--coverage",
action="store_true",
help="Generate coverage report"
)
parser.add_argument(
"-p", "--parallel",
action="store_true",
help="Run tests in parallel"
)
parser.add_argument(
"-v", "--verbose",
action="store_true",
help="Verbose output"
"-p", "--parallel", action="store_true", help="Run tests in parallel"
)
parser.add_argument("-v", "--verbose", action="store_true", help="Verbose output")
args = parser.parse_args()
# Build command
@ -71,10 +55,10 @@ def main():
# Print command
print(f"Running: {' '.join(cmd)}")
# Execute command
result = subprocess.run(cmd)
# Check coverage thresholds if coverage is enabled
if args.coverage:
print("Checking coverage thresholds...")
@ -83,9 +67,9 @@ def main():
if coverage_result.returncode != 0:
print("Coverage below threshold (90%)")
sys.exit(coverage_result.returncode)
sys.exit(result.returncode)
if __name__ == "__main__":
main()
main()