Skip to content
Draft
Show file tree
Hide file tree
Changes from 52 commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
b4f565b
cherry pick claim related files
rudransh-shrivastava Jun 20, 2026
731811c
display spinner when fetching data, show toast for reorder, fixes
rudransh-shrivastava Jun 20, 2026
1af6996
backend: update claim mutations to return claim object
rudransh-shrivastava Jun 20, 2026
bd4a54a
select claim/claims in mutations
rudransh-shrivastava Jun 21, 2026
e80f85e
avoid refetching queries on mutation, use returned claim object
rudransh-shrivastava Jun 21, 2026
a15bf82
add timeout to toasts
rudransh-shrivastava Jun 21, 2026
ab08b9c
remove unused queries temporarily
rudransh-shrivastava Jun 21, 2026
3d5f326
reduce queries network calls by merging queries
rudransh-shrivastava Jun 21, 2026
625db7b
use status enum instead of strings
rudransh-shrivastava Jun 21, 2026
867c94f
add claim type
rudransh-shrivastava Jun 21, 2026
974318e
backend: add has_evidence
rudransh-shrivastava Jun 21, 2026
3d65c52
add has evidence label
rudransh-shrivastava Jun 21, 2026
88af465
add unit tests for bod dashboard page
rudransh-shrivastava Jun 21, 2026
b5e6c6d
add tests
rudransh-shrivastava Jun 21, 2026
497af6c
add e2e tests
rudransh-shrivastava Jun 21, 2026
00ae269
undo backend change
rudransh-shrivastava Jun 21, 2026
79d1a7c
bot comments
rudransh-shrivastava Jun 21, 2026
bb605c7
bot comments
rudransh-shrivastava Jun 21, 2026
93a7d07
improve skip condition
rudransh-shrivastava Jun 21, 2026
6370b08
backend: annotate has_evidence
rudransh-shrivastava Jun 21, 2026
4157b4e
Merge branch 'feature/bod-candidate-transparency' into feature/bod-cl…
rudransh-shrivastava Jun 21, 2026
f0112e9
apply bot comments
rudransh-shrivastava Jun 22, 2026
d68477c
improve breadcrumbs
rudransh-shrivastava Jun 22, 2026
7b56553
backend: add test for annotate has evidence
rudransh-shrivastava Jun 22, 2026
6fe2194
update code
rudransh-shrivastava Jun 22, 2026
c6ea05b
apply bot suggestions
rudransh-shrivastava Jun 22, 2026
fd34abb
fix tests
rudransh-shrivastava Jun 23, 2026
6fbd993
improve test coverage
rudransh-shrivastava Jun 23, 2026
8f4fe72
fix e2e test
rudransh-shrivastava Jun 23, 2026
640208f
fmt frontend files
rudransh-shrivastava Jun 23, 2026
55466a9
fix sonar qube and bot issues
rudransh-shrivastava Jun 23, 2026
b502f9b
fix double click on keypress
rudransh-shrivastava Jun 23, 2026
ee4005b
update code
rudransh-shrivastava Jun 24, 2026
bcc61fa
add disableAnimation to Button
rudransh-shrivastava Jun 24, 2026
9c34d81
cherry pick evidence related files
rudransh-shrivastava Jun 24, 2026
2caede4
update pnpm lock
rudransh-shrivastava Jun 24, 2026
29097a0
add back missing query and generate
rudransh-shrivastava Jun 24, 2026
a2073d2
fix skip: condition
rudransh-shrivastava Jun 24, 2026
4808531
remove refetchQueries and use mutation returned object
rudransh-shrivastava Jun 25, 2026
1c3c434
fix validation by removing .
rudransh-shrivastava Jun 25, 2026
1ba8581
fix isSyncing bug
rudransh-shrivastava Jun 25, 2026
3eff335
move access denied display up
rudransh-shrivastava Jun 25, 2026
719325c
fix titles
rudransh-shrivastava Jun 25, 2026
71fa26b
update code
rudransh-shrivastava Jun 25, 2026
abc3ec6
remove double queries
rudransh-shrivastava Jun 25, 2026
1de83ec
add unit tests
rudransh-shrivastava Jun 26, 2026
2c6eb8b
add e2e tests
rudransh-shrivastava Jun 26, 2026
bebd592
bot comments
rudransh-shrivastava Jun 26, 2026
7642e6e
fix packages
rudransh-shrivastava Jun 27, 2026
1971106
claim: improvements to claim related code
rudransh-shrivastava Jun 27, 2026
f84694a
claim: add annotate to single node query
rudransh-shrivastava Jun 27, 2026
73a00d6
apply bot suggestions
rudransh-shrivastava Jun 27, 2026
c076ec0
claim: bot comments
rudransh-shrivastava Jun 27, 2026
e782776
update status to use enum
rudransh-shrivastava Jun 27, 2026
251b0c3
fix tests
rudransh-shrivastava Jun 27, 2026
26b1cc4
show toast on download file error
rudransh-shrivastava Jun 27, 2026
23f9e2f
clear errors when file is selected
rudransh-shrivastava Jun 27, 2026
018cdfe
bot comments
rudransh-shrivastava Jun 27, 2026
e196122
clear mocks before use
rudransh-shrivastava Jun 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class ClaimResult:
ok: bool
code: str | None = None
message: str | None = None
claim: BoardCandidateClaimNode | None = None


def _validate_reorder_claims(
Expand Down Expand Up @@ -167,7 +168,7 @@ def create_board_candidate_claim(
)

try:
BoardCandidateClaim.objects.create(
claim = BoardCandidateClaim.objects.create(
board=board,
candidate=candidate,
description=input_data.description,
Expand All @@ -194,7 +195,12 @@ def create_board_candidate_claim(
message=" ".join(messages),
)

return ClaimResult(ok=True, code="SUCCESS", message="Claim created successfully.")
return ClaimResult(
ok=True,
code="SUCCESS",
message="Claim created successfully.",
claim=claim,
)

@strawberry.mutation(permission_classes=[IsAuthenticated])
@transaction.atomic
Expand Down Expand Up @@ -250,7 +256,12 @@ def update_board_candidate_claim(
message=" ".join(messages),
)

return ClaimResult(ok=True, code="SUCCESS", message="Claim updated successfully.")
return ClaimResult(
ok=True,
code="SUCCESS",
message="Claim updated successfully.",
claim=claim,
)

@strawberry.mutation(permission_classes=[IsAuthenticated])
@transaction.atomic
Expand Down Expand Up @@ -302,7 +313,12 @@ def discard_board_candidate_claim(
message=" ".join(messages),
)

return ClaimResult(ok=True, code="SUCCESS", message="Claim discarded successfully.")
return ClaimResult(
ok=True,
code="SUCCESS",
message="Claim discarded successfully.",
claim=claim,
)

@strawberry.mutation(permission_classes=[IsAuthenticated])
@transaction.atomic
Expand Down Expand Up @@ -366,6 +382,7 @@ def submit_board_candidate_claim(
ok=True,
code="SUCCESS",
message="Claim submitted successfully.",
claim=claim,
)

return result
Expand Down Expand Up @@ -425,7 +442,12 @@ def withdraw_board_candidate_claim(
message=" ".join(messages),
)

return ClaimResult(ok=True, code="SUCCESS", message="Claim withdrawn successfully.")
return ClaimResult(
ok=True,
code="SUCCESS",
message="Claim withdrawn successfully.",
claim=claim,
)

@strawberry.mutation(permission_classes=[IsAuthenticated])
@transaction.atomic
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,13 @@ def created_at(self, root: BoardCandidateClaim) -> datetime:
"""Resolve claim creation date."""
return root.nest_created_at

@strawberry_django.field
def has_evidence(self, root: BoardCandidateClaim) -> bool:
"""Resolve whether the claim has any evidence."""
if hasattr(root, "evidence_exists"):
return root.evidence_exists
return root.evidences.filter(is_removed=False).exists()

@strawberry_django.field
def status(self, root: BoardCandidateClaim) -> ClaimStatusEnum:
"""Resolve claim status as GraphQL enum."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@

import strawberry
import strawberry_django
from django.db.models import Exists, OuterRef

from apps.owasp.api.internal.nodes.board_candidate_claim import BoardCandidateClaimNode
from apps.owasp.models.board_candidate_claim import BoardCandidateClaim
from apps.owasp.models.board_candidate_claim_evidence import BoardCandidateClaimEvidence


@strawberry.type
Expand Down Expand Up @@ -32,10 +34,20 @@ def board_candidate_claims(
and user.github_user is not None
and user.github_user.login == login
)
claims = BoardCandidateClaim.objects.filter(
board__year=year,
candidate__member__login=login,
).order_by("order", "nest_created_at")
claims = (
BoardCandidateClaim.objects.filter(
board__year=year,
candidate__member__login=login,
)
.annotate(
evidence_exists=Exists(
BoardCandidateClaimEvidence.objects.filter(
claim=OuterRef("pk"), is_removed=False
)
),
)
.order_by("order", "nest_created_at")
)

if not is_self:
claims = claims.filter(status=BoardCandidateClaim.Status.APPROVED)
Expand All @@ -59,7 +71,13 @@ def board_candidate_claim(

"""
try:
claim = BoardCandidateClaim.objects.get(
claim = BoardCandidateClaim.objects.annotate(
evidence_exists=Exists(
BoardCandidateClaimEvidence.objects.filter(
claim=OuterRef("pk"), is_removed=False
)
),
).get(
board__year=year,
candidate__member__login=login,
key=key,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ def test_discard_claim_success(self, mock_claim_model):

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.status == BoardCandidateClaim.Status.DISCARDED
claim.save.assert_called_once()

Expand Down Expand Up @@ -130,6 +131,7 @@ def test_submit_claim_success(self, mock_claim_model):

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.status == BoardCandidateClaim.Status.SUBMITTED
claim.save.assert_called_once()

Expand Down Expand Up @@ -210,6 +212,7 @@ def test_withdraw_claim_success_submitted(self, mock_timezone, mock_claim_model)

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.status == BoardCandidateClaim.Status.WITHDRAWN
assert claim.withdrawn_reason == "No longer relevant"
assert claim.withdrawn_at == now
Expand Down Expand Up @@ -238,6 +241,7 @@ def test_withdraw_claim_success_approved(self, mock_timezone, mock_claim_model):

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.status == BoardCandidateClaim.Status.WITHDRAWN
assert claim.withdrawn_reason == "No longer relevant"
assert claim.withdrawn_at == now
Expand Down Expand Up @@ -548,6 +552,7 @@ def test_create_claim_success(self, mock_claim_model, mock_board_model):
)
assert result.ok
assert result.code == "SUCCESS"
assert result.claim is not None

@patch("apps.owasp.api.internal.mutations.board_candidate_claim.BoardOfDirectors")
def test_create_claim_board_not_found(self, mock_board_model):
Expand Down Expand Up @@ -688,6 +693,7 @@ def test_update_claim_success(self, mock_claim_model):

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.name == input_data.name
assert claim.description == input_data.description
claim.save.assert_called_once()
Expand All @@ -713,6 +719,7 @@ def test_update_claim_partial(self, mock_claim_model):

assert result.ok
assert result.code == "SUCCESS"
assert result.claim is claim
assert claim.name == "Updated Name"
claim.save.assert_called_once_with(update_fields=["name", "key"])

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
"""Tests for BoardCandidateClaim GraphQL node."""

from unittest.mock import Mock

from apps.owasp.api.internal.nodes.board_candidate_claim import BoardCandidateClaimNode
from tests.unit.apps.common.graphql_node_base_test import GraphQLNodeBaseTest

Expand All @@ -15,6 +17,7 @@ def test_node_fields(self):
"_id",
"created_at",
"description",
"has_evidence",
"is_locked",
"key",
"name",
Expand All @@ -25,3 +28,32 @@ def test_node_fields(self):
"withdrawn_reason",
}
assert field_names == expected_field_names

def test_has_evidence_returns_true_when_evidence_exists(self):
mock_claim = Mock()
mock_claim.evidence_exists = True

field = self._get_field_by_name("has_evidence", BoardCandidateClaimNode)
result = field.base_resolver.wrapped_func(None, mock_claim)

assert result

def test_has_evidence_returns_false_when_no_evidence(self):
mock_claim = Mock()
mock_claim.evidence_exists = False

field = self._get_field_by_name("has_evidence", BoardCandidateClaimNode)
result = field.base_resolver.wrapped_func(None, mock_claim)

assert not result

def test_has_evidence_falls_back_to_evidences_filter_when_annotation_missing(self):
mock_claim = Mock(spec=[])
mock_claim.evidences = Mock()
mock_claim.evidences.filter.return_value.exists.return_value = True

field = self._get_field_by_name("has_evidence", BoardCandidateClaimNode)
result = field.base_resolver.wrapped_func(None, mock_claim)

mock_claim.evidences.filter.assert_called_once_with(is_removed=False)
assert result
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def test_board_candidate_claims_self(self, mock_claim_model):

claims = [MagicMock(), MagicMock()]
mock_qs = MagicMock()
mock_qs.annotate.return_value = mock_qs
mock_qs.order_by.return_value = claims
Comment thread
rudransh-shrivastava marked this conversation as resolved.
mock_claim_model.objects.filter.return_value = mock_qs

Expand All @@ -48,6 +49,7 @@ def test_board_candidate_claims_non_self_filters_approved(self, mock_claim_model
info = _make_info(user)

base_qs = MagicMock()
base_qs.annotate.return_value = base_qs
ordered_qs = MagicMock()
filtered_qs = MagicMock()
ordered_qs.filter.return_value = filtered_qs
Expand All @@ -68,6 +70,7 @@ def test_board_candidate_claims_anonymous_filters_approved(self, mock_claim_mode
info = _make_info(user)

base_qs = MagicMock()
base_qs.annotate.return_value = base_qs
ordered_qs = MagicMock()
filtered_qs = MagicMock()
ordered_qs.filter.return_value = filtered_qs
Expand Down Expand Up @@ -97,12 +100,15 @@ def test_board_candidate_claim_self(self, mock_claim_model):
claim = MagicMock()
claim.candidate.member = mock_github_user
claim.status = BoardCandidateClaim.Status.DRAFT
mock_claim_model.objects.get.return_value = claim
mock_qs = MagicMock()
mock_qs.get.return_value = claim
mock_claim_model.objects.annotate.return_value = mock_qs
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.

query = BoardCandidateClaimQuery()
result = query.board_candidate_claim(info, login="alice", key="test-key", year=2025)

mock_claim_model.objects.get.assert_called_once_with(
mock_claim_model.objects.annotate.assert_called_once()
mock_qs.get.assert_called_once_with(
candidate__member__login="alice", key="test-key", board__year=2025
)
assert result == claim
Expand All @@ -119,11 +125,14 @@ def test_board_candidate_claim_non_self_approved(self, mock_claim_model):
claim = MagicMock()
claim.candidate.member = MagicMock()
claim.status = BoardCandidateClaim.Status.APPROVED
mock_claim_model.objects.get.return_value = claim
mock_qs = MagicMock()
mock_qs.get.return_value = claim
mock_claim_model.objects.annotate.return_value = mock_qs

query = BoardCandidateClaimQuery()
result = query.board_candidate_claim(info, login="alice", key="test-key", year=2025)

mock_claim_model.objects.annotate.assert_called_once()
assert result == claim

@patch("apps.owasp.api.internal.queries.board_candidate_claim.BoardCandidateClaim")
Expand All @@ -138,7 +147,9 @@ def test_board_candidate_claim_non_self_not_approved(self, mock_claim_model):
claim = MagicMock()
claim.candidate.member = MagicMock()
claim.status = BoardCandidateClaim.Status.SUBMITTED
mock_claim_model.objects.get.return_value = claim
mock_qs = MagicMock()
mock_qs.get.return_value = claim
mock_claim_model.objects.annotate.return_value = mock_qs

query = BoardCandidateClaimQuery()
result = query.board_candidate_claim(info, login="alice", key="test-key", year=2025)
Expand All @@ -154,7 +165,9 @@ def test_board_candidate_claim_not_found(self, mock_claim_model):
user.github_user = MagicMock()
info = _make_info(user)

mock_claim_model.objects.get.side_effect = BoardCandidateClaim.DoesNotExist
mock_qs = MagicMock()
mock_qs.get.side_effect = BoardCandidateClaim.DoesNotExist
mock_claim_model.objects.annotate.return_value = mock_qs

query = BoardCandidateClaimQuery()
result = query.board_candidate_claim(info, login="alice", key="test-key", year=2025)
Expand All @@ -167,13 +180,9 @@ def test_board_candidate_claim_anonymous_approved(self, mock_claim_model):
mock_claim_model.DoesNotExist = BoardCandidateClaim.DoesNotExist
user = MagicMock()
user.is_authenticated = False
info = _make_info(user)

claim = MagicMock()
claim.status = BoardCandidateClaim.Status.APPROVED
mock_claim_model.objects.get.return_value = claim

query = BoardCandidateClaimQuery()
result = query.board_candidate_claim(info, login="alice", key="test-key", year=2025)

assert result == claim
mock_qs = MagicMock()
mock_qs.get.return_value = claim
mock_claim_model.objects.annotate.return_value = mock_qs
32 changes: 32 additions & 0 deletions e2e/helpers/mockAuthCookies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
export const authCookies = [
{
name: 'csrftoken',
value: 'abc123',
domain: 'localhost',
path: '/',
},
{
name: 'nest.session-id',
value: 'test-session-id',
domain: 'localhost',
path: '/',
},
{
name: 'next-auth.csrf-token',
value: 'test-csrf-token',
domain: 'localhost',
path: '/',
},
{
name: 'next-auth.callback-url',
value: '/',
domain: 'localhost',
path: '/',
},
{
name: 'next-auth.session-token',
value: 'test-session-token',
domain: 'localhost',
path: '/',
},
]
Loading
Loading