ФУНКЦИИ - МЕЖПРОЕКТНАЯ КОММУНИКАЦИЯ: additive read-layer двусторонней доски внешних контуров

This commit is contained in:
DCCONSTRUCTIONS
2026-04-20 20:31:02 +03:00
parent f12a3b7338
commit 0184ff9a32
7 changed files with 582 additions and 7 deletions
@@ -0,0 +1,156 @@
# Copyright (c) 2023-present Plane Software, Inc. and contributors
# SPDX-License-Identifier: AGPL-3.0-only
# See the LICENSE file for details.
import pytest
from rest_framework import status
from rest_framework.test import APIClient
from plane.db.models import Intake, IntakeIssue, Issue, Project, ProjectMember, State, WorkspaceMember
from plane.db.models.intake import IntakeIssueStatus
from plane.db.models.project import ROLE
from plane.db.models.state import StateGroup
@pytest.mark.contract
class TestExternalContoursBoardAPI:
def get_board_url(self, workspace_slug: str, project_id) -> str:
return f"/api/workspaces/{workspace_slug}/projects/{project_id}/external-contours/board/"
def get_board_item_url(self, workspace_slug: str, project_id, request_id) -> str:
return f"/api/workspaces/{workspace_slug}/projects/{project_id}/external-contours/board-items/{request_id}/"
def create_project_with_member(self, workspace, user, name: str, identifier: str, intake_view: bool = False) -> Project:
project = Project.objects.create(
name=name,
identifier=identifier,
workspace=workspace,
intake_view=intake_view,
)
ProjectMember.objects.create(project=project, member=user, role=ROLE.ADMIN.value, is_active=True)
return project
def create_state(self, project: Project, name: str, group: str, color: str, default: bool = False) -> State:
return State.objects.create(
project=project,
workspace=project.workspace,
name=name,
group=group,
color=color,
sequence=1000,
default=default,
)
def create_external_contour_request(self, source_project: Project, target_project: Project, requested_by, state: State):
intake = Intake.objects.create(
name="External Contours Bridge",
project=target_project,
is_default=False,
)
issue = Issue.objects.create(
project=target_project,
state=state,
name="Cross-project request",
description_html="<p>Need help</p>",
priority="high",
created_by=requested_by,
)
return IntakeIssue.objects.create(
intake=intake,
project=target_project,
issue=issue,
status=IntakeIssueStatus.ACCEPTED.value,
extra={
"bridge": "external-contours",
"source_project_id": str(source_project.id),
"source_project_name": source_project.name,
"target_project_id": str(target_project.id),
"target_project_name": target_project.name,
"requested_by_id": str(requested_by.id),
"requested_by_name": requested_by.display_name,
"requested_at": issue.created_at.isoformat() if issue.created_at else None,
},
)
@pytest.mark.django_db
def test_source_project_member_gets_outgoing_board_items(self, workspace, create_user):
source_user = create_user
target_user = type(source_user).objects.create_user(email="target-board@example.com", username="target-board")
WorkspaceMember.objects.create(workspace=workspace, member=target_user, role=ROLE.ADMIN.value, is_active=True)
source_project = self.create_project_with_member(workspace, source_user, "Source Board", "SRCBRD")
target_project = self.create_project_with_member(workspace, target_user, "Target Board", "TGTBRD", intake_view=True)
open_state = self.create_state(target_project, "Todo", StateGroup.UNSTARTED.value, "#cccccc", default=True)
contour_request = self.create_external_contour_request(source_project, target_project, source_user, open_state)
client = APIClient()
client.force_authenticate(user=source_user)
response = client.get(self.get_board_url(workspace.slug, source_project.id), format="json")
assert response.status_code == status.HTTP_200_OK
columns = {column["key"]: column for column in response.data["columns"]}
assert columns["outgoing"]["total_count"] == 1
assert columns["incoming"]["total_count"] == 0
assert columns["outgoing"]["results"][0]["id"] == str(contour_request.id)
assert columns["outgoing"]["results"][0]["direction"] == "outgoing"
assert columns["outgoing"]["results"][0]["capabilities"]["can_open_target_issue"] is False
@pytest.mark.django_db
def test_target_project_member_gets_incoming_board_items(self, workspace, create_user):
source_user = create_user
target_user = type(source_user).objects.create_user(email="incoming-board@example.com", username="incoming-board")
WorkspaceMember.objects.create(workspace=workspace, member=target_user, role=ROLE.ADMIN.value, is_active=True)
source_project = self.create_project_with_member(workspace, source_user, "Source Incoming", "SRCINC")
target_project = self.create_project_with_member(workspace, target_user, "Target Incoming", "TGTINC", intake_view=True)
open_state = self.create_state(target_project, "Todo", StateGroup.UNSTARTED.value, "#cccccc", default=True)
contour_request = self.create_external_contour_request(source_project, target_project, source_user, open_state)
client = APIClient()
client.force_authenticate(user=target_user)
response = client.get(self.get_board_url(workspace.slug, target_project.id), format="json")
assert response.status_code == status.HTTP_200_OK
columns = {column["key"]: column for column in response.data["columns"]}
assert columns["outgoing"]["total_count"] == 0
assert columns["incoming"]["total_count"] == 1
assert columns["incoming"]["results"][0]["id"] == str(contour_request.id)
assert columns["incoming"]["results"][0]["direction"] == "incoming"
assert columns["incoming"]["results"][0]["capabilities"]["can_open_target_issue"] is True
@pytest.mark.django_db
def test_board_item_detail_resolves_perspective_and_capabilities(self, workspace, create_user):
source_user = create_user
target_user = type(source_user).objects.create_user(email="detail-board@example.com", username="detail-board")
WorkspaceMember.objects.create(workspace=workspace, member=target_user, role=ROLE.ADMIN.value, is_active=True)
source_project = self.create_project_with_member(workspace, source_user, "Source Detail", "SRCDET")
target_project = self.create_project_with_member(workspace, target_user, "Target Detail", "TGTDET", intake_view=True)
closed_state = self.create_state(target_project, "Done", StateGroup.COMPLETED.value, "#00ff00", default=True)
contour_request = self.create_external_contour_request(source_project, target_project, source_user, closed_state)
source_client = APIClient()
source_client.force_authenticate(user=source_user)
source_response = source_client.get(
self.get_board_item_url(workspace.slug, source_project.id, contour_request.id),
format="json",
)
assert source_response.status_code == status.HTTP_200_OK
assert source_response.data["direction"] == "outgoing"
assert source_response.data["capabilities"]["can_source_decide"] is True
assert source_response.data["capabilities"]["can_open_target_issue"] is False
target_client = APIClient()
target_client.force_authenticate(user=target_user)
target_response = target_client.get(
self.get_board_item_url(workspace.slug, target_project.id, contour_request.id),
format="json",
)
assert target_response.status_code == status.HTTP_200_OK
assert target_response.data["direction"] == "incoming"
assert target_response.data["capabilities"]["can_source_decide"] is False
assert target_response.data["capabilities"]["can_open_target_issue"] is True