Files
2024-02-15 17:01:50 +00:00

224 lines
8.1 KiB
Python

from __future__ import annotations
from time import time
from typing import Any
from unittest.mock import Mock, patch
from urllib.parse import parse_qs
import pytest
import responses
from django.forms import ChoiceField
from django.http import HttpRequest
from sentry.identity.vsts.provider import VSTSIdentityProvider, VSTSOAuth2CallbackView
from sentry.integrations.vsts.integration import AccountConfigView, AccountForm
from sentry.models.identity import Identity
from sentry.testutils.cases import TestCase
from sentry.testutils.silo import control_silo_test
@control_silo_test
class TestVSTSOAuthCallbackView(TestCase):
@responses.activate
def test_exchange_token(self):
def redirect_url():
return "https://app.vssps.visualstudio.com/oauth2/authorize"
view = VSTSOAuth2CallbackView(
access_token_url="https://app.vssps.visualstudio.com/oauth2/token",
client_id="vsts-client-id",
client_secret="vsts-client-secret",
)
request = Mock()
pipeline = Mock()
pipeline.redirect_url = redirect_url
responses.add(
responses.POST,
"https://app.vssps.visualstudio.com/oauth2/token",
json={
"access_token": "xxxxxxxxx",
"token_type": "jwt-bearer",
"expires_in": "3599",
"refresh_token": "zzzzzzzzzz",
},
)
result = view.exchange_token(request, pipeline, "oauth-code")
mock_request = responses.calls[0].request
req_params = parse_qs(mock_request.body)
assert req_params["grant_type"] == ["urn:ietf:params:oauth:grant-type:jwt-bearer"]
assert req_params["assertion"] == ["oauth-code"]
assert req_params["redirect_uri"] == ["https://app.vssps.visualstudio.com/oauth2/authorize"]
assert req_params["client_assertion_type"] == [
"urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
]
assert req_params["client_assertion"] == ["vsts-client-secret"]
assert result["access_token"] == "xxxxxxxxx"
assert result["token_type"] == "jwt-bearer"
assert result["expires_in"] == "3599"
assert result["refresh_token"] == "zzzzzzzzzz"
@control_silo_test
class TestAccountConfigView(TestCase):
def setUp(self):
responses.reset()
account_id = "1234567-8910"
self.base_url = "http://sentry2.visualstudio.com/"
self.accounts: list[dict[str, Any]] = [
{
"accountId": "1234567-89",
"NamespaceId": "00000000-0000-0000-0000-000000000000",
"accountName": "sentry",
"OrganizationName": None,
"AccountType": 0,
"AccountOwner": "00000000-0000-0000-0000-000000000000",
"CreatedBy": "00000000-0000-0000-0000-000000000000",
"CreatedDate": "0001-01-01T00:00:00",
"AccountStatus": 0,
"StatusReason": None,
"LastUpdatedBy": "00000000-0000-0000-0000-000000000000",
"Properties": {},
},
{
"accountId": account_id,
"NamespaceId": "00000000-0000-0000-0000-000000000000",
"accountName": "sentry2",
"OrganizationName": None,
"AccountType": 0,
"AccountOwner": "00000000-0000-0000-0000-000000000000",
"CreatedBy": "00000000-0000-0000-0000-000000000000",
"CreatedDate": "0001-01-01T00:00:00",
"AccountStatus": 0,
"StatusReason": None,
"LastUpdatedBy": "00000000-0000-0000-0000-000000000000",
"Properties": {},
},
]
responses.add(
responses.GET,
"https://app.vssps.visualstudio.com/_apis/accounts",
json={"value": self.accounts, "count": len(self.accounts)},
status=200,
)
responses.add(
responses.GET,
"https://app.vssps.visualstudio.com/_apis/resourceareas/79134C72-4A58-4B42-976C-04E7115F32BF?hostId=%s&api-preview=5.0-preview.1"
% account_id,
json={"locationUrl": self.base_url},
)
@responses.activate
def test_dispatch(self):
view = AccountConfigView()
request = HttpRequest()
request.POST.update({"account": "1234567-8910"})
pipeline = Mock()
pipeline.state = {
"accounts": self.accounts,
"identity": {"data": {"access_token": "123456789"}},
}
pipeline.fetch_state = lambda key: pipeline.state[key]
pipeline.bind_state = lambda name, value: pipeline.state.update({name: value})
view.dispatch(request, pipeline)
assert pipeline.fetch_state(key="account") == self.accounts[1]
assert pipeline.next_step.call_count == 1
@responses.activate
def test_get_accounts(self):
view = AccountConfigView()
accounts = view.get_accounts("access-token", 123)
assert accounts is not None
assert accounts["value"][0]["accountName"] == "sentry"
assert accounts["value"][1]["accountName"] == "sentry2"
@responses.activate
def test_account_form(self):
account_form = AccountForm(self.accounts)
field = account_form.fields["account"]
assert isinstance(field, ChoiceField)
assert field.choices == [
("1234567-89", "sentry"),
("1234567-8910", "sentry2"),
]
@responses.activate
@patch("sentry.integrations.vsts.integration.get_user_info")
@patch("sentry.integrations.vsts.integration.render_to_response")
def test_no_accounts_received(self, mock_render_to_response, mock_get_user_info):
responses.reset()
responses.add(
responses.GET,
"https://app.vssps.visualstudio.com/_apis/accounts",
json={"value": [], "count": 0},
status=200,
)
view = AccountConfigView()
request = Mock()
request.POST = {}
request.user = self.user
pipeline = Mock()
pipeline.fetch_state = lambda key: {"data": {"access_token": "1234567890"}}
pipeline.organization = self.organization
view.dispatch(request, pipeline)
assert mock_get_user_info.called is True
assert mock_render_to_response.called is True
assert mock_render_to_response.call_args[1]["context"] == {"no_accounts": True}
@control_silo_test
class VstsIdentityProviderTest(TestCase):
client_secret = "12345678"
def setUp(self):
self.identity_provider_model = self.create_identity_provider(type="vsts")
self.identity = Identity.objects.create(
idp=self.identity_provider_model,
user=self.user,
external_id="vsts_id",
data={
"access_token": "123456789",
"token_type": "token_type",
"expires": 12345678,
"refresh_token": "n354678",
},
)
self.provider = VSTSIdentityProvider()
@pytest.fixture(autouse=True)
def patch_get_oauth_client_secret(self):
with patch.object(
VSTSIdentityProvider, "get_oauth_client_secret", return_value=self.client_secret
):
yield
@responses.activate
def test_refresh_identity(self):
refresh_data = {
"access_token": "access token for this user",
"token_type": "type of token",
"expires": 1234567,
"refresh_token": "new refresh token to use when the token has timed out",
}
responses.add(
responses.POST, "https://app.vssps.visualstudio.com/oauth2/token", json=refresh_data
)
self.provider.refresh_identity(self.identity, redirect_url="redirect_url")
assert len(responses.calls) == 1
new_identity = Identity.objects.get(id=self.identity.id)
assert new_identity.data["access_token"] == refresh_data["access_token"]
assert new_identity.data["token_type"] == refresh_data["token_type"]
assert new_identity.data["expires"] <= int(time())