Skip to content

Commit 3de52db

Browse files
grichaclaude
andcommitted
test(integrations): Add ViewerContext tests for MS Teams webhook
Verify that webhook_viewer_context sets the correct organization_id and actor_type during both action submission and team member removal. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 1421bf4 commit 3de52db

File tree

2 files changed

+74
-0
lines changed

2 files changed

+74
-0
lines changed

tests/sentry/integrations/msteams/test_action_state_change.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from sentry.silo.base import SiloMode
2323
from sentry.testutils.asserts import assert_mock_called_once_with_partial, assert_slo_metric
2424
from sentry.testutils.cases import APITestCase
25+
from sentry.testutils.helpers.options import override_options
2526
from sentry.testutils.silo import assume_test_silo_mode
2627
from sentry.testutils.skips import requires_snuba
2728
from sentry.users.models.identity import Identity, IdentityStatus
@@ -359,6 +360,31 @@ def test_resolve_with_params(self, verify: MagicMock, client_put: MagicMock) ->
359360

360361
assert_mock_called_once_with_partial(client_put, data=expected_data)
361362

363+
@responses.activate
364+
@override_options({"viewer-context.enabled": True})
365+
@patch("sentry.integrations.msteams.webhook.verify_signature", return_value=True)
366+
def test_action_submitted_sets_viewer_context(self, verify: MagicMock) -> None:
367+
"""ViewerContext is set with org_id and actor_type=INTEGRATION during action handling."""
368+
from sentry.viewer_context import ActorType, get_viewer_context
369+
370+
captured_contexts: list = []
371+
372+
original_refresh = Group.refresh_from_db
373+
374+
def capturing_refresh(self_group, *args, **kwargs):
375+
captured_contexts.append(get_viewer_context())
376+
return original_refresh(self_group, *args, **kwargs)
377+
378+
with patch.object(Group, "refresh_from_db", capturing_refresh):
379+
resp = self.post_webhook(action_type=ACTION_TYPE.RESOLVE, resolve_input="resolved")
380+
381+
assert resp.status_code == 200
382+
assert len(captured_contexts) == 1
383+
ctx = captured_contexts[0]
384+
assert ctx is not None
385+
assert ctx.organization_id == self.org.id
386+
assert ctx.actor_type == ActorType.INTEGRATION
387+
362388
@responses.activate
363389
@patch("sentry.integrations.msteams.webhook.verify_signature", return_value=True)
364390
def test_no_integration(self, verify: MagicMock) -> None:

tests/sentry/integrations/msteams/test_webhook.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from sentry.silo.base import SiloMode
1616
from sentry.testutils.asserts import assert_slo_metric
1717
from sentry.testutils.cases import APITestCase
18+
from sentry.testutils.helpers.options import override_options
1819
from sentry.testutils.silo import assume_test_silo_mode
1920
from sentry.users.models.identity import Identity
2021
from sentry.utils import jwt
@@ -608,3 +609,50 @@ def test_invalid_silo_card_action_payload(
608609
HTTP_AUTHORIZATION=f"Bearer {TOKEN}",
609610
)
610611
assert response.status_code == 400
612+
613+
@responses.activate
614+
@override_options({"viewer-context.enabled": True})
615+
@mock.patch("sentry.utils.jwt.decode")
616+
@mock.patch("time.time")
617+
def test_member_removed_sets_viewer_context(
618+
self, mock_time: MagicMock, mock_decode: MagicMock
619+
) -> None:
620+
"""ViewerContext is set with org_id and actor_type=INTEGRATION during member removal."""
621+
from sentry.viewer_context import ActorType, get_viewer_context
622+
623+
with assume_test_silo_mode(SiloMode.CONTROL):
624+
integration = self.create_provider_integration(external_id=team_id, provider="msteams")
625+
self.create_organization_integration(
626+
organization_id=self.organization.id, integration=integration
627+
)
628+
629+
captured_contexts: list = []
630+
631+
original_create_audit_entry = __import__(
632+
"sentry.utils.audit", fromlist=["create_audit_entry"]
633+
).create_audit_entry
634+
635+
def capturing_create_audit_entry(*args, **kwargs):
636+
captured_contexts.append(get_viewer_context())
637+
return original_create_audit_entry(*args, **kwargs)
638+
639+
mock_time.return_value = 1594839999 + 60
640+
mock_decode.return_value = DECODED_TOKEN
641+
642+
with mock.patch(
643+
"sentry.integrations.msteams.webhook.create_audit_entry",
644+
side_effect=capturing_create_audit_entry,
645+
):
646+
resp = self.client.post(
647+
path=webhook_url,
648+
data=EXAMPLE_TEAM_MEMBER_REMOVED,
649+
format="json",
650+
HTTP_AUTHORIZATION=f"Bearer {TOKEN}",
651+
)
652+
653+
assert resp.status_code == 204
654+
assert len(captured_contexts) == 1
655+
ctx = captured_contexts[0]
656+
assert ctx is not None
657+
assert ctx.organization_id == self.organization.id
658+
assert ctx.actor_type == ActorType.INTEGRATION

0 commit comments

Comments
 (0)