chore(): switched all fields in contract to snake_case and linting improvements
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from typing import override
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
@@ -7,13 +8,14 @@ from apps.users.auth.jwt import (
|
||||
create_token_pair,
|
||||
)
|
||||
from apps.users.models import User, UserRole
|
||||
from apps.users.tests._helpers import _auth_header, _make_user
|
||||
from apps.users.tests.helpers import auth_header, make_user
|
||||
|
||||
|
||||
class AuthAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.user: User = _make_user(
|
||||
self.user: User = make_user(
|
||||
username="api_auth",
|
||||
email="api_auth@x.com",
|
||||
password="testpass123",
|
||||
@@ -92,7 +94,7 @@ class AuthAPITest(TestCase):
|
||||
def test_me_authenticated(self) -> None:
|
||||
resp = self.client.get(
|
||||
reverse("api-1:me"),
|
||||
HTTP_AUTHORIZATION=_auth_header(self.user),
|
||||
HTTP_AUTHORIZATION=auth_header(self.user),
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
|
||||
@@ -47,7 +47,7 @@ def handle_validation_error(
|
||||
{
|
||||
"field": field,
|
||||
"issue": error.get("msg", "Unknown error"),
|
||||
"rejectedValue": error.get("input"),
|
||||
"rejected_value": error.get("input"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -77,7 +77,7 @@ def handle_django_validation_error(
|
||||
{
|
||||
"field": field,
|
||||
"issue": str(error.message),
|
||||
"rejectedValue": None,
|
||||
"rejected_value": None,
|
||||
}
|
||||
for error in errors
|
||||
)
|
||||
@@ -86,7 +86,7 @@ def handle_django_validation_error(
|
||||
{
|
||||
"field": "non_field_error",
|
||||
"issue": str(error.message),
|
||||
"rejectedValue": None,
|
||||
"rejected_value": None,
|
||||
}
|
||||
for error in exc.error_list
|
||||
)
|
||||
|
||||
@@ -9,8 +9,6 @@ from apps.users.models import User
|
||||
|
||||
|
||||
class ApproverOut(ModelSchema):
|
||||
first_name: str = Field("", alias="firstName")
|
||||
last_name: str = Field("", alias="lastName")
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
@@ -55,12 +53,10 @@ class ApproverGroupOut(ModelSchema):
|
||||
class ApproverGroupCreateIn(Schema):
|
||||
experimenter_id: str = Field(
|
||||
...,
|
||||
alias="experimenterId",
|
||||
description="UUID of the experimenter user this group belongs to.",
|
||||
)
|
||||
approver_ids: list[str] = Field(
|
||||
default_factory=list,
|
||||
alias="approverIds",
|
||||
description=(
|
||||
"List of user UUIDs to add as approvers. "
|
||||
"Each user must have the 'approver' role."
|
||||
@@ -68,7 +64,6 @@ class ApproverGroupCreateIn(Schema):
|
||||
)
|
||||
min_approvals: int = Field(
|
||||
1,
|
||||
alias="minApprovals",
|
||||
ge=1,
|
||||
description=(
|
||||
"Number of distinct approvals required. "
|
||||
@@ -80,7 +75,6 @@ class ApproverGroupCreateIn(Schema):
|
||||
class ApproverGroupUpdateIn(Schema):
|
||||
approver_ids: list[str] | None = Field(
|
||||
None,
|
||||
alias="approverIds",
|
||||
description=(
|
||||
"If provided, replaces the current set of approvers. "
|
||||
"Each user must have the 'approver' role."
|
||||
@@ -88,7 +82,6 @@ class ApproverGroupUpdateIn(Schema):
|
||||
)
|
||||
min_approvals: int | None = Field(
|
||||
None,
|
||||
alias="minApprovals",
|
||||
ge=1,
|
||||
description="New minimum approval threshold.",
|
||||
)
|
||||
@@ -102,7 +95,6 @@ class ApproverGroupListOut(Schema):
|
||||
class ApproverGroupAddApproverIn(Schema):
|
||||
approver_id: str = Field(
|
||||
...,
|
||||
alias="approverId",
|
||||
description="UUID of the user to add as an approver.",
|
||||
)
|
||||
|
||||
@@ -110,7 +102,6 @@ class ApproverGroupAddApproverIn(Schema):
|
||||
class ApproverGroupRemoveApproverIn(Schema):
|
||||
approver_id: str = Field(
|
||||
...,
|
||||
alias="approverId",
|
||||
description="UUID of the approver to remove.",
|
||||
)
|
||||
|
||||
@@ -133,13 +124,11 @@ class ReviewSettingsOut(ModelSchema):
|
||||
class ReviewSettingsUpdateIn(Schema):
|
||||
default_min_approvals: int | None = Field(
|
||||
None,
|
||||
alias="defaultMinApprovals",
|
||||
ge=1,
|
||||
description="New default minimum approval threshold.",
|
||||
)
|
||||
allow_any_approver: bool | None = Field(
|
||||
None,
|
||||
alias="allowAnyApprover",
|
||||
description="New fallback policy for approver eligibility.",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,34 +1,35 @@
|
||||
import json
|
||||
import uuid
|
||||
from typing import override
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.reviews.models import ApproverGroup
|
||||
from apps.reviews.services import approver_group_create, review_settings_update
|
||||
from apps.reviews.tests._helpers import (
|
||||
_get,
|
||||
_make_admin,
|
||||
_make_approver,
|
||||
_make_experimenter,
|
||||
_make_viewer,
|
||||
from apps.reviews.tests.helpers import (
|
||||
make_admin,
|
||||
make_approver,
|
||||
make_experimenter,
|
||||
make_viewer,
|
||||
)
|
||||
from apps.users.models import User
|
||||
from apps.users.tests._helpers import _auth_header
|
||||
from apps.users.tests.helpers import auth_header
|
||||
|
||||
|
||||
class ApproverGroupAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_api")
|
||||
self.viewer: User = _make_viewer("_api")
|
||||
self.experimenter: User = _make_experimenter("_api")
|
||||
self.approver1: User = _make_approver("_api1")
|
||||
self.approver2: User = _make_approver("_api2")
|
||||
self.approver3: User = _make_approver("_api3")
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.viewer_auth: str = _auth_header(self.viewer)
|
||||
self.exp_auth: str = _auth_header(self.experimenter)
|
||||
self.admin: User = make_admin("_api")
|
||||
self.viewer: User = make_viewer("_api")
|
||||
self.experimenter: User = make_experimenter("_api")
|
||||
self.approver1: User = make_approver("_api1")
|
||||
self.approver2: User = make_approver("_api2")
|
||||
self.approver3: User = make_approver("_api3")
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
self.viewer_auth: str = auth_header(self.viewer)
|
||||
self.exp_auth: str = auth_header(self.experimenter)
|
||||
|
||||
def test_list_groups_admin(self) -> None:
|
||||
approver_group_create(
|
||||
@@ -57,7 +58,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
self.assertEqual(resp.status_code, 401)
|
||||
|
||||
def test_list_groups_pagination(self) -> None:
|
||||
exp2: User = _make_experimenter("_api2")
|
||||
exp2: User = make_experimenter("_api2")
|
||||
approver_group_create(experimenter=self.experimenter)
|
||||
approver_group_create(experimenter=exp2)
|
||||
resp = self.client.get(
|
||||
@@ -74,12 +75,12 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"approverIds": [
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"approver_ids": [
|
||||
str(self.approver1.pk),
|
||||
str(self.approver2.pk),
|
||||
],
|
||||
"minApprovals": 2,
|
||||
"min_approvals": 2,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -87,7 +88,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 201)
|
||||
data = resp.json()
|
||||
self.assertEqual(_get(data, "minApprovals", "min_approvals"), 2)
|
||||
self.assertEqual(data["min_approvals"], 2)
|
||||
self.assertEqual(len(data["approvers"]), 2)
|
||||
self.assertEqual(data["experimenter"]["id"], str(self.experimenter.pk))
|
||||
|
||||
@@ -96,8 +97,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -112,8 +113,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -126,8 +127,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -140,8 +141,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(uuid.uuid4()),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(uuid.uuid4()),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -154,8 +155,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.viewer.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.viewer.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -168,8 +169,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -179,8 +180,8 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -193,9 +194,9 @@ class ApproverGroupAPITest(TestCase):
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(self.experimenter.pk),
|
||||
"approverIds": [str(self.viewer.pk)],
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(self.experimenter.pk),
|
||||
"approver_ids": [str(self.viewer.pk)],
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -203,7 +204,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
)
|
||||
self.assertIn(resp.status_code, [422, 400])
|
||||
|
||||
def test_get_group_admin(self) -> None:
|
||||
def testget_group_admin(self) -> None:
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=self.experimenter,
|
||||
approver_ids=[str(self.approver1.pk)],
|
||||
@@ -220,7 +221,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
self.assertEqual(data["id"], str(group.pk))
|
||||
self.assertEqual(len(data["approvers"]), 1)
|
||||
|
||||
def test_get_group_not_found(self) -> None:
|
||||
def testget_group_not_found(self) -> None:
|
||||
resp = self.client.get(
|
||||
reverse(
|
||||
"api-1:get_approver_group",
|
||||
@@ -230,7 +231,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
def test_get_group_viewer_denied(self) -> None:
|
||||
def testget_group_viewer_denied(self) -> None:
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=self.experimenter
|
||||
)
|
||||
@@ -256,11 +257,11 @@ class ApproverGroupAPITest(TestCase):
|
||||
),
|
||||
data=json.dumps(
|
||||
{
|
||||
"approverIds": [
|
||||
"approver_ids": [
|
||||
str(self.approver1.pk),
|
||||
str(self.approver2.pk),
|
||||
],
|
||||
"minApprovals": 2,
|
||||
"min_approvals": 2,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -268,7 +269,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
self.assertEqual(_get(data, "minApprovals", "min_approvals"), 2)
|
||||
self.assertEqual(data["min_approvals"], 2)
|
||||
self.assertEqual(len(data["approvers"]), 2)
|
||||
|
||||
def test_update_group_partial_min_approvals(self) -> None:
|
||||
@@ -285,13 +286,13 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:update_approver_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"minApprovals": 2}),
|
||||
data=json.dumps({"min_approvals": 2}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
self.assertEqual(_get(data, "minApprovals", "min_approvals"), 2)
|
||||
self.assertEqual(data["min_approvals"], 2)
|
||||
|
||||
def test_update_group_not_found(self) -> None:
|
||||
resp = self.client.patch(
|
||||
@@ -299,7 +300,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:update_approver_group",
|
||||
kwargs={"group_id": str(uuid.uuid4())},
|
||||
),
|
||||
data=json.dumps({"minApprovals": 1}),
|
||||
data=json.dumps({"min_approvals": 1}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -314,7 +315,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:update_approver_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"minApprovals": 1}),
|
||||
data=json.dumps({"min_approvals": 1}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.viewer_auth,
|
||||
)
|
||||
@@ -368,7 +369,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver2.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver2.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -381,7 +382,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(uuid.uuid4())},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver1.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver1.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -396,7 +397,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(uuid.uuid4())}),
|
||||
data=json.dumps({"approver_id": str(uuid.uuid4())}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -411,7 +412,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.viewer.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.viewer.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -427,7 +428,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver1.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver1.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -442,7 +443,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver1.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver1.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.viewer_auth,
|
||||
)
|
||||
@@ -462,7 +463,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:remove_approver_from_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver2.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver2.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -480,7 +481,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:remove_approver_from_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver1.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver1.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -496,7 +497,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:remove_approver_from_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver2.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver2.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -515,7 +516,7 @@ class ApproverGroupAPITest(TestCase):
|
||||
"api-1:remove_approver_from_group",
|
||||
kwargs={"group_id": str(group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver1.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver1.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.viewer_auth,
|
||||
)
|
||||
@@ -523,14 +524,15 @@ class ApproverGroupAPITest(TestCase):
|
||||
|
||||
|
||||
class ApproverGroupByExperimenterAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_byexp")
|
||||
self.experimenter: User = _make_experimenter("_byexp")
|
||||
self.approver: User = _make_approver("_byexp")
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.admin: User = make_admin("_byexp")
|
||||
self.experimenter: User = make_experimenter("_byexp")
|
||||
self.approver: User = make_approver("_byexp")
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
|
||||
def test_get_by_experimenter_found(self) -> None:
|
||||
def testget_by_experimenter_found(self) -> None:
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=self.experimenter,
|
||||
approver_ids=[str(self.approver.pk)],
|
||||
@@ -545,8 +547,8 @@ class ApproverGroupByExperimenterAPITest(TestCase):
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
self.assertEqual(resp.json()["id"], str(group.pk))
|
||||
|
||||
def test_get_by_experimenter_not_found(self) -> None:
|
||||
exp2: User = _make_experimenter("_byexp2")
|
||||
def testget_by_experimenter_not_found(self) -> None:
|
||||
exp2: User = make_experimenter("_byexp2")
|
||||
resp = self.client.get(
|
||||
reverse(
|
||||
"api-1:get_approver_group_by_experimenter",
|
||||
@@ -556,7 +558,7 @@ class ApproverGroupByExperimenterAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
def test_get_by_experimenter_invalid_id(self) -> None:
|
||||
def testget_by_experimenter_invalid_id(self) -> None:
|
||||
resp = self.client.get(
|
||||
reverse(
|
||||
"api-1:get_approver_group_by_experimenter",
|
||||
@@ -566,7 +568,7 @@ class ApproverGroupByExperimenterAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
def test_get_by_experimenter_unauthenticated(self) -> None:
|
||||
def testget_by_experimenter_unauthenticated(self) -> None:
|
||||
resp = self.client.get(
|
||||
reverse(
|
||||
"api-1:get_approver_group_by_experimenter",
|
||||
@@ -577,30 +579,29 @@ class ApproverGroupByExperimenterAPITest(TestCase):
|
||||
|
||||
|
||||
class ReviewSettingsAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_rs")
|
||||
self.viewer: User = _make_viewer("_rs")
|
||||
self.experimenter: User = _make_experimenter("_rs")
|
||||
self.approver: User = _make_approver("_rs")
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.viewer_auth: str = _auth_header(self.viewer)
|
||||
self.exp_auth: str = _auth_header(self.experimenter)
|
||||
self.appr_auth: str = _auth_header(self.approver)
|
||||
self.admin: User = make_admin("_rs")
|
||||
self.viewer: User = make_viewer("_rs")
|
||||
self.experimenter: User = make_experimenter("_rs")
|
||||
self.approver: User = make_approver("_rs")
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
self.viewer_auth: str = auth_header(self.viewer)
|
||||
self.exp_auth: str = auth_header(self.experimenter)
|
||||
self.appr_auth: str = auth_header(self.approver)
|
||||
|
||||
def test_get_settings_admin(self) -> None:
|
||||
def testget_settings_admin(self) -> None:
|
||||
resp = self.client.get(
|
||||
reverse("api-1:get_review_settings"),
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
min_val = _get(data, "defaultMinApprovals", "default_min_approvals")
|
||||
any_val = _get(data, "allowAnyApprover", "allow_any_approver")
|
||||
self.assertEqual(min_val, 1)
|
||||
self.assertFalse(any_val)
|
||||
self.assertEqual(data["default_min_approvals"], 1)
|
||||
self.assertFalse(data["allow_any_approver"])
|
||||
|
||||
def test_get_settings_any_authenticated_role(self) -> None:
|
||||
def testget_settings_any_authenticated_role(self) -> None:
|
||||
for auth in [self.viewer_auth, self.exp_auth, self.appr_auth]:
|
||||
resp = self.client.get(
|
||||
reverse("api-1:get_review_settings"),
|
||||
@@ -608,7 +609,7 @@ class ReviewSettingsAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
|
||||
def test_get_settings_unauthenticated(self) -> None:
|
||||
def testget_settings_unauthenticated(self) -> None:
|
||||
resp = self.client.get(reverse("api-1:get_review_settings"))
|
||||
self.assertEqual(resp.status_code, 401)
|
||||
|
||||
@@ -616,34 +617,31 @@ class ReviewSettingsAPITest(TestCase):
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps(
|
||||
{"defaultMinApprovals": 3, "allowAnyApprover": False}
|
||||
{"default_min_approvals": 3, "allow_any_approver": False}
|
||||
),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
min_val = _get(data, "defaultMinApprovals", "default_min_approvals")
|
||||
any_val = _get(data, "allowAnyApprover", "allow_any_approver")
|
||||
self.assertEqual(min_val, 3)
|
||||
self.assertFalse(any_val)
|
||||
self.assertEqual(data["default_min_approvals"], 3)
|
||||
self.assertFalse(data["allow_any_approver"])
|
||||
|
||||
def test_update_settings_partial(self) -> None:
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 5}),
|
||||
data=json.dumps({"default_min_approvals": 5}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
min_val = _get(data, "defaultMinApprovals", "default_min_approvals")
|
||||
self.assertEqual(min_val, 5)
|
||||
self.assertEqual(data["default_min_approvals"], 5)
|
||||
|
||||
def test_update_settings_viewer_denied(self) -> None:
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 2}),
|
||||
data=json.dumps({"default_min_approvals": 2}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.viewer_auth,
|
||||
)
|
||||
@@ -652,7 +650,7 @@ class ReviewSettingsAPITest(TestCase):
|
||||
def test_update_settings_experimenter_denied(self) -> None:
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 2}),
|
||||
data=json.dumps({"default_min_approvals": 2}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.exp_auth,
|
||||
)
|
||||
@@ -661,7 +659,7 @@ class ReviewSettingsAPITest(TestCase):
|
||||
def test_update_settings_approver_denied(self) -> None:
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 2}),
|
||||
data=json.dumps({"default_min_approvals": 2}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.appr_auth,
|
||||
)
|
||||
@@ -670,7 +668,7 @@ class ReviewSettingsAPITest(TestCase):
|
||||
def test_update_settings_invalid_min_approvals(self) -> None:
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 0}),
|
||||
data=json.dumps({"default_min_approvals": 0}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -678,15 +676,16 @@ class ReviewSettingsAPITest(TestCase):
|
||||
|
||||
|
||||
class EffectivePolicyAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_pol")
|
||||
self.experimenter: User = _make_experimenter("_pol")
|
||||
self.exp_no_group: User = _make_experimenter("_pol2")
|
||||
self.approver: User = _make_approver("_pol")
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.viewer: User = _make_viewer("_pol")
|
||||
self.viewer_auth: str = _auth_header(self.viewer)
|
||||
self.admin: User = make_admin("_pol")
|
||||
self.experimenter: User = make_experimenter("_pol")
|
||||
self.exp_no_group: User = make_experimenter("_pol2")
|
||||
self.approver: User = make_approver("_pol")
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
self.viewer: User = make_viewer("_pol")
|
||||
self.viewer_auth: str = auth_header(self.viewer)
|
||||
|
||||
self.group: ApproverGroup = approver_group_create(
|
||||
experimenter=self.experimenter,
|
||||
@@ -704,13 +703,10 @@ class EffectivePolicyAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
exp_id = _get(data, "experimenterId", "experimenter_id")
|
||||
min_val = _get(data, "minApprovals", "min_approvals")
|
||||
has_group = _get(data, "hasExplicitGroup", "has_explicit_group")
|
||||
self.assertEqual(exp_id, str(self.experimenter.pk))
|
||||
self.assertEqual(min_val, 1)
|
||||
self.assertEqual(data["experimenter_id"], str(self.experimenter.pk))
|
||||
self.assertEqual(data["min_approvals"], 1)
|
||||
self.assertEqual(data["source"], "approver_group")
|
||||
self.assertTrue(has_group)
|
||||
self.assertTrue(data["has_explicit_group"])
|
||||
self.assertEqual(len(data["approvers"]), 1)
|
||||
self.assertEqual(data["approvers"][0]["id"], str(self.approver.pk))
|
||||
|
||||
@@ -727,11 +723,9 @@ class EffectivePolicyAPITest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
min_val = _get(data, "minApprovals", "min_approvals")
|
||||
has_group = _get(data, "hasExplicitGroup", "has_explicit_group")
|
||||
self.assertEqual(data["source"], "global_fallback")
|
||||
self.assertFalse(has_group)
|
||||
self.assertEqual(min_val, 2)
|
||||
self.assertFalse(data["has_explicit_group"])
|
||||
self.assertEqual(data["min_approvals"], 2)
|
||||
|
||||
def test_effective_policy_fallback_deny(self) -> None:
|
||||
review_settings_update(allow_any_approver=False)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from typing import override
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.test import Client, TestCase
|
||||
@@ -15,27 +16,28 @@ from apps.reviews.services import (
|
||||
approver_group_update,
|
||||
review_settings_update,
|
||||
)
|
||||
from apps.reviews.tests._helpers import (
|
||||
_make_admin,
|
||||
_make_approver,
|
||||
_make_experimenter,
|
||||
_make_viewer,
|
||||
from apps.reviews.tests.helpers import (
|
||||
make_admin,
|
||||
make_approver,
|
||||
make_experimenter,
|
||||
make_viewer,
|
||||
)
|
||||
from apps.users.models import User
|
||||
from apps.users.tests._helpers import _auth_header
|
||||
from apps.users.tests.helpers import auth_header
|
||||
|
||||
|
||||
class ReviewRBACTest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_rbac")
|
||||
self.experimenter: User = _make_experimenter("_rbac")
|
||||
self.approver: User = _make_approver("_rbac")
|
||||
self.viewer: User = _make_viewer("_rbac")
|
||||
self.admin: User = make_admin("_rbac")
|
||||
self.experimenter: User = make_experimenter("_rbac")
|
||||
self.approver: User = make_approver("_rbac")
|
||||
self.viewer: User = make_viewer("_rbac")
|
||||
self.non_admin_users: dict[str, str] = {
|
||||
"experimenter": _auth_header(self.experimenter),
|
||||
"approver": _auth_header(self.approver),
|
||||
"viewer": _auth_header(self.viewer),
|
||||
"experimenter": auth_header(self.experimenter),
|
||||
"approver": auth_header(self.approver),
|
||||
"viewer": auth_header(self.viewer),
|
||||
}
|
||||
self.group: ApproverGroup = approver_group_create(
|
||||
experimenter=self.experimenter,
|
||||
@@ -52,13 +54,13 @@ class ReviewRBACTest(TestCase):
|
||||
|
||||
def test_create_group_denied_for_non_admins(self) -> None:
|
||||
for role_name, auth in self.non_admin_users.items():
|
||||
exp2: User = _make_experimenter(f"_rbac_cr_{role_name}")
|
||||
exp2: User = make_experimenter(f"_rbac_cr_{role_name}")
|
||||
resp = self.client.post(
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(exp2.pk),
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(exp2.pk),
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -84,7 +86,7 @@ class ReviewRBACTest(TestCase):
|
||||
"api-1:update_approver_group",
|
||||
kwargs={"group_id": str(self.group.pk)},
|
||||
),
|
||||
data=json.dumps({"minApprovals": 1}),
|
||||
data=json.dumps({"min_approvals": 1}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=auth,
|
||||
)
|
||||
@@ -102,14 +104,14 @@ class ReviewRBACTest(TestCase):
|
||||
self.assertEqual(resp.status_code, 403, role_name)
|
||||
|
||||
def test_add_approver_denied_for_non_admins(self) -> None:
|
||||
appr2: User = _make_approver("_rbac_add")
|
||||
appr2: User = make_approver("_rbac_add")
|
||||
for role_name, auth in self.non_admin_users.items():
|
||||
resp = self.client.post(
|
||||
reverse(
|
||||
"api-1:add_approver_to_group",
|
||||
kwargs={"group_id": str(self.group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(appr2.pk)}),
|
||||
data=json.dumps({"approver_id": str(appr2.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=auth,
|
||||
)
|
||||
@@ -122,7 +124,7 @@ class ReviewRBACTest(TestCase):
|
||||
"api-1:remove_approver_from_group",
|
||||
kwargs={"group_id": str(self.group.pk)},
|
||||
),
|
||||
data=json.dumps({"approverId": str(self.approver.pk)}),
|
||||
data=json.dumps({"approver_id": str(self.approver.pk)}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=auth,
|
||||
)
|
||||
@@ -132,7 +134,7 @@ class ReviewRBACTest(TestCase):
|
||||
for role_name, auth in self.non_admin_users.items():
|
||||
resp = self.client.put(
|
||||
reverse("api-1:update_review_settings"),
|
||||
data=json.dumps({"defaultMinApprovals": 99}),
|
||||
data=json.dumps({"default_min_approvals": 99}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=auth,
|
||||
)
|
||||
@@ -140,14 +142,15 @@ class ReviewRBACTest(TestCase):
|
||||
|
||||
|
||||
class ReviewEdgeCasesTest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_admin("_edge")
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.admin: User = make_admin("_edge")
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
|
||||
def test_delete_group_then_fallback_applies(self) -> None:
|
||||
exp: User = _make_experimenter("_edge1")
|
||||
appr: User = _make_approver("_edge1")
|
||||
exp: User = make_experimenter("_edge1")
|
||||
appr: User = make_approver("_edge1")
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=exp,
|
||||
approver_ids=[str(appr.pk)],
|
||||
@@ -162,8 +165,8 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertEqual(min_app, 3)
|
||||
|
||||
def test_inactive_approver_excluded_from_effective_policy(self) -> None:
|
||||
exp: User = _make_experimenter("_edge2")
|
||||
appr: User = _make_approver("_edge2")
|
||||
exp: User = make_experimenter("_edge2")
|
||||
appr: User = make_approver("_edge2")
|
||||
approver_group_create(
|
||||
experimenter=exp,
|
||||
approver_ids=[str(appr.pk)],
|
||||
@@ -174,8 +177,8 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertEqual(approvers.count(), 0)
|
||||
|
||||
def test_create_group_with_all_three_approvers(self) -> None:
|
||||
exp: User = _make_experimenter("_edge3")
|
||||
apprs: list[User] = [_make_approver(f"_edge3_{i}") for i in range(3)]
|
||||
exp: User = make_experimenter("_edge3")
|
||||
apprs: list[User] = [make_approver(f"_edge3_{i}") for i in range(3)]
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=exp,
|
||||
approver_ids=[str(a.pk) for a in apprs],
|
||||
@@ -185,8 +188,8 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertEqual(group.min_approvals, 2)
|
||||
|
||||
def test_update_group_to_empty_approvers(self) -> None:
|
||||
exp: User = _make_experimenter("_edge4")
|
||||
appr: User = _make_approver("_edge4")
|
||||
exp: User = make_experimenter("_edge4")
|
||||
appr: User = make_approver("_edge4")
|
||||
group: ApproverGroup = approver_group_create(
|
||||
experimenter=exp,
|
||||
approver_ids=[str(appr.pk)],
|
||||
@@ -198,15 +201,15 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
)
|
||||
|
||||
def test_api_output_format_approver_group(self) -> None:
|
||||
exp: User = _make_experimenter("_edge5")
|
||||
appr: User = _make_approver("_edge5")
|
||||
exp: User = make_experimenter("_edge5")
|
||||
appr: User = make_approver("_edge5")
|
||||
resp = self.client.post(
|
||||
reverse("api-1:create_approver_group"),
|
||||
data=json.dumps(
|
||||
{
|
||||
"experimenterId": str(exp.pk),
|
||||
"approverIds": [str(appr.pk)],
|
||||
"minApprovals": 1,
|
||||
"experimenter_id": str(exp.pk),
|
||||
"approver_ids": [str(appr.pk)],
|
||||
"min_approvals": 1,
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
@@ -217,9 +220,9 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertIn("id", data)
|
||||
self.assertIn("experimenter", data)
|
||||
self.assertIn("approvers", data)
|
||||
self.assertTrue("minApprovals" in data or "min_approvals" in data)
|
||||
self.assertTrue("createdAt" in data or "created_at" in data)
|
||||
self.assertTrue("updatedAt" in data or "updated_at" in data)
|
||||
self.assertIn("min_approvals", data)
|
||||
self.assertIn("created_at", data)
|
||||
self.assertIn("updated_at", data)
|
||||
self.assertIn("id", data["experimenter"])
|
||||
self.assertIn("username", data["experimenter"])
|
||||
self.assertIn("email", data["experimenter"])
|
||||
@@ -237,17 +240,13 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
self.assertIn("id", data)
|
||||
self.assertTrue(
|
||||
"defaultMinApprovals" in data or "default_min_approvals" in data
|
||||
)
|
||||
self.assertTrue(
|
||||
"allowAnyApprover" in data or "allow_any_approver" in data
|
||||
)
|
||||
self.assertTrue("updatedAt" in data or "updated_at" in data)
|
||||
self.assertIn("default_min_approvals", data)
|
||||
self.assertIn("allow_any_approver", data)
|
||||
self.assertIn("updated_at", data)
|
||||
|
||||
def test_api_output_format_effective_policy(self) -> None:
|
||||
exp: User = _make_experimenter("_edge6")
|
||||
appr: User = _make_approver("_edge6")
|
||||
exp: User = make_experimenter("_edge6")
|
||||
appr: User = make_approver("_edge6")
|
||||
approver_group_create(
|
||||
experimenter=exp,
|
||||
approver_ids=[str(appr.pk)],
|
||||
@@ -261,19 +260,17 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
)
|
||||
self.assertEqual(resp.status_code, 200)
|
||||
data = resp.json()
|
||||
self.assertTrue("experimenterId" in data or "experimenter_id" in data)
|
||||
self.assertTrue("minApprovals" in data or "min_approvals" in data)
|
||||
self.assertIn("experimenter_id", data)
|
||||
self.assertIn("min_approvals", data)
|
||||
self.assertIn("approvers", data)
|
||||
self.assertIn("source", data)
|
||||
self.assertTrue(
|
||||
"hasExplicitGroup" in data or "has_explicit_group" in data
|
||||
)
|
||||
self.assertIn("has_explicit_group", data)
|
||||
|
||||
def test_multiple_experimenters_independent_groups(self) -> None:
|
||||
exp1: User = _make_experimenter("_edge_m1")
|
||||
exp2: User = _make_experimenter("_edge_m2")
|
||||
appr1: User = _make_approver("_edge_m1")
|
||||
appr2: User = _make_approver("_edge_m2")
|
||||
exp1: User = make_experimenter("_edge_m1")
|
||||
exp2: User = make_experimenter("_edge_m2")
|
||||
appr1: User = make_approver("_edge_m1")
|
||||
appr2: User = make_approver("_edge_m2")
|
||||
|
||||
g1: ApproverGroup = approver_group_create(
|
||||
experimenter=exp1,
|
||||
@@ -293,9 +290,9 @@ class ReviewEdgeCasesTest(TestCase):
|
||||
self.assertFalse(g2.can_approve(appr1))
|
||||
|
||||
def test_concurrent_fallback_and_explicit_group(self) -> None:
|
||||
exp_with: User = _make_experimenter("_edge_c1")
|
||||
exp_without: User = _make_experimenter("_edge_c2")
|
||||
appr: User = _make_approver("_edge_c")
|
||||
exp_with: User = make_experimenter("_edge_c1")
|
||||
exp_without: User = make_experimenter("_edge_c2")
|
||||
appr: User = make_approver("_edge_c")
|
||||
|
||||
approver_group_create(
|
||||
experimenter=exp_with,
|
||||
|
||||
@@ -8,6 +8,7 @@ from ninja.renderers import BaseRenderer
|
||||
|
||||
from api.v1 import handlers
|
||||
from api.v1.auth.endpoints import router as auth_router
|
||||
from api.v1.flags.endpoints import router as flags_router
|
||||
from api.v1.reviews.endpoints import router as reviews_router
|
||||
from api.v1.users.endpoints import router as users_router
|
||||
|
||||
@@ -23,7 +24,7 @@ class ORJSONRenderer(BaseRenderer):
|
||||
|
||||
def default(self, obj: Any) -> Any:
|
||||
if isinstance(obj, Schema):
|
||||
return obj.model_dump(by_alias=True)
|
||||
return obj.model_dump()
|
||||
raise TypeError
|
||||
|
||||
|
||||
@@ -41,6 +42,11 @@ router.add_router(
|
||||
auth_router,
|
||||
)
|
||||
|
||||
router.add_router(
|
||||
"flags",
|
||||
flags_router,
|
||||
)
|
||||
|
||||
router.add_router(
|
||||
"users",
|
||||
users_router,
|
||||
|
||||
@@ -12,18 +12,18 @@ class FieldError(Schema):
|
||||
)
|
||||
issue: str = Field(..., description="Problem description")
|
||||
rejected_value: Any = Field(
|
||||
None, alias="rejectedValue", description="Value that failed validation"
|
||||
None, description="Value that failed validation"
|
||||
)
|
||||
|
||||
|
||||
class ApiError(Schema):
|
||||
code: str
|
||||
message: str
|
||||
trace_id: str = Field(..., alias="traceId")
|
||||
trace_id: str
|
||||
timestamp: datetime
|
||||
path: str
|
||||
details: dict[str, Any] | None = None
|
||||
|
||||
|
||||
class ValidationError(ApiError):
|
||||
field_errors: list[FieldError] = Field(..., alias="fieldErrors")
|
||||
field_errors: list[FieldError]
|
||||
|
||||
@@ -38,7 +38,7 @@ router = Router(tags=["users"])
|
||||
def list_users(
|
||||
request: HttpRequest,
|
||||
role: str | None = None,
|
||||
is_active: bool | None = None,
|
||||
is_active: bool | None = None, # noqa: FBT001
|
||||
search: str | None = None,
|
||||
limit: int = 50,
|
||||
offset: int = 0,
|
||||
|
||||
@@ -7,8 +7,7 @@ from apps.users.models import User
|
||||
|
||||
|
||||
class UserOut(ModelSchema):
|
||||
first_name: str = Field("", alias="firstName")
|
||||
last_name: str = Field("", alias="lastName")
|
||||
|
||||
is_active: bool
|
||||
|
||||
class Meta:
|
||||
@@ -52,13 +51,11 @@ class UserCreateIn(Schema):
|
||||
)
|
||||
first_name: str = Field(
|
||||
"",
|
||||
alias="firstName",
|
||||
max_length=150,
|
||||
description="First name.",
|
||||
)
|
||||
last_name: str = Field(
|
||||
"",
|
||||
alias="lastName",
|
||||
max_length=150,
|
||||
description="Last name.",
|
||||
)
|
||||
@@ -91,19 +88,16 @@ class UserUpdateIn(Schema):
|
||||
)
|
||||
first_name: str | None = Field(
|
||||
None,
|
||||
alias="firstName",
|
||||
max_length=150,
|
||||
description="New first name.",
|
||||
)
|
||||
last_name: str | None = Field(
|
||||
None,
|
||||
alias="lastName",
|
||||
max_length=150,
|
||||
description="New last name.",
|
||||
)
|
||||
is_active: bool | None = Field(
|
||||
None,
|
||||
alias="isActive",
|
||||
description="Set active/inactive status.",
|
||||
)
|
||||
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
from typing import override
|
||||
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from apps.users.models import User, UserRole
|
||||
from apps.users.tests._helpers import _auth_header, _make_user
|
||||
from apps.users.tests.helpers import auth_header, make_user
|
||||
|
||||
|
||||
class BaseUsersAPITest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.admin: User = _make_user(
|
||||
self.admin: User = make_user(
|
||||
username="mgmt_admin",
|
||||
email="mgmt_admin@x.com",
|
||||
password="adminpass1",
|
||||
role=UserRole.ADMIN,
|
||||
)
|
||||
self.viewer: User = _make_user(
|
||||
self.viewer: User = make_user(
|
||||
username="mgmt_viewer",
|
||||
email="mgmt_viewer@x.com",
|
||||
password="viewerpass",
|
||||
role=UserRole.VIEWER,
|
||||
)
|
||||
self.admin_auth: str = _auth_header(self.admin)
|
||||
self.viewer_auth: str = _auth_header(self.viewer)
|
||||
self.admin_auth: str = auth_header(self.admin)
|
||||
self.viewer_auth: str = auth_header(self.viewer)
|
||||
|
||||
@@ -4,14 +4,14 @@ import uuid
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.users.models import User, UserRole
|
||||
from apps.users.tests._helpers import _make_user
|
||||
from apps.users.tests.helpers import make_user
|
||||
|
||||
from ._crud_base import BaseUsersAPITest
|
||||
|
||||
|
||||
class UsersAPIDeleteAssignRoleTest(BaseUsersAPITest):
|
||||
def test_delete_user_admin(self) -> None:
|
||||
target: User = _make_user(
|
||||
target: User = make_user(
|
||||
username="to_delete",
|
||||
email="del@lotty.local",
|
||||
role=UserRole.VIEWER,
|
||||
|
||||
@@ -66,8 +66,8 @@ class UsersAPIListCreateTest(BaseUsersAPITest):
|
||||
"email": "new@lotty.local",
|
||||
"password": "newpass123",
|
||||
"role": "experimenter",
|
||||
"firstName": "New",
|
||||
"lastName": "User",
|
||||
"first_name": "New",
|
||||
"last_name": "User",
|
||||
}
|
||||
),
|
||||
content_type="application/json",
|
||||
|
||||
@@ -51,7 +51,7 @@ class UsersAPIReadUpdateTest(BaseUsersAPITest):
|
||||
reverse(
|
||||
"api-1:update_user", kwargs={"user_id": str(self.viewer.pk)}
|
||||
),
|
||||
data=json.dumps({"firstName": "Updated"}),
|
||||
data=json.dumps({"first_name": "Updated"}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
@@ -75,7 +75,7 @@ class UsersAPIReadUpdateTest(BaseUsersAPITest):
|
||||
reverse(
|
||||
"api-1:update_user", kwargs={"user_id": str(self.admin.pk)}
|
||||
),
|
||||
data=json.dumps({"firstName": "Hacked"}),
|
||||
data=json.dumps({"first_name": "Hacked"}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.viewer_auth,
|
||||
)
|
||||
@@ -86,7 +86,7 @@ class UsersAPIReadUpdateTest(BaseUsersAPITest):
|
||||
reverse(
|
||||
"api-1:update_user", kwargs={"user_id": str(uuid.uuid4())}
|
||||
),
|
||||
data=json.dumps({"firstName": "Ghost"}),
|
||||
data=json.dumps({"first_name": "Ghost"}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.admin_auth,
|
||||
)
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import json
|
||||
from typing import override
|
||||
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
from apps.users.models import User, UserRole
|
||||
from apps.users.tests._helpers import _auth_header, _make_user
|
||||
from apps.users.tests.helpers import auth_header, make_user
|
||||
|
||||
|
||||
class RoleBasedAccessControlTest(TestCase):
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.client = Client()
|
||||
self.roles = {}
|
||||
@@ -17,7 +19,7 @@ class RoleBasedAccessControlTest(TestCase):
|
||||
UserRole.APPROVER,
|
||||
UserRole.VIEWER,
|
||||
]:
|
||||
user: User = _make_user(
|
||||
user: User = make_user(
|
||||
username=f"rbac_{role_val}",
|
||||
email=f"rbac_{role_val}@x.com",
|
||||
password="password1",
|
||||
@@ -25,7 +27,7 @@ class RoleBasedAccessControlTest(TestCase):
|
||||
)
|
||||
self.roles[role_val] = {
|
||||
"user": user,
|
||||
"auth": _auth_header(user),
|
||||
"auth": auth_header(user),
|
||||
}
|
||||
|
||||
def test_admin_can_list(self) -> None:
|
||||
@@ -93,7 +95,7 @@ class RoleBasedAccessControlTest(TestCase):
|
||||
target = self.roles[UserRole.VIEWER]["user"]
|
||||
resp = self.client.patch(
|
||||
reverse("api-1:update_user", kwargs={"user_id": str(target.pk)}),
|
||||
data=json.dumps({"firstName": "Nope"}),
|
||||
data=json.dumps({"first_name": "Nope"}),
|
||||
content_type="application/json",
|
||||
HTTP_AUTHORIZATION=self.roles[UserRole.EXPERIMENTER]["auth"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user