File: test_vscode_credential.py

package info (click to toggle)
python-azure 20251118%2Bgit-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 783,356 kB
  • sloc: python: 6,474,533; ansic: 804; javascript: 287; sh: 205; makefile: 198; xml: 109
file content (186 lines) | stat: -rw-r--r-- 7,988 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# ------------------------------------
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.
# ------------------------------------
import json
import os
import platform
import tempfile
from unittest.mock import patch

import pytest

from azure.core.exceptions import ClientAuthenticationError
from azure.identity import AuthenticationRecord, CredentialUnavailableError
from azure.identity._constants import AZURE_VSCODE_CLIENT_ID, KnownAuthorities
from azure.identity._credentials.vscode import VisualStudioCodeCredential, load_vscode_auth_record

# Skip all tests in this module when running on PyPy
pytestmark = pytest.mark.skipif(
    platform.python_implementation() == "PyPy", reason="Broker tests are not supported on PyPy"
)


class TestVisualStudioCodeCredential:
    """Test cases for VisualStudioCodeCredential"""

    def test_get_token_info(self):
        """Test getting a token from the credential."""
        valid_data = {
            "tenantId": "12345678-1234-1234-1234-123456789012",
            "clientId": AZURE_VSCODE_CLIENT_ID,
            "username": "user@example.com",
            "homeAccountId": "user.tenant",
            "authority": KnownAuthorities.AZURE_PUBLIC_CLOUD,
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            json.dump(valid_data, tmp_file)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with patch("msal.PublicClientApplication"):
                    with patch("msal.PublicClientApplication.acquire_token_interactive"):
                        credential = VisualStudioCodeCredential()
                        with pytest.raises(ClientAuthenticationError):
                            credential.get_token_info("https://management.azure.com/.default")
        finally:
            os.unlink(tmp_file.name)

    def test_invalid_auth_record(self):
        """Test that an error is raised if the auth record is nonexistent/invalid."""

        # Test with a nonexistent file
        with patch("os.path.expanduser", return_value="nonexistent_file.json"):
            with pytest.raises(CredentialUnavailableError):
                VisualStudioCodeCredential().get_token_info("https://management.azure.com/.default")

        # Test with invalid data (incorrect client ID)
        invalid_data = {
            "tenantId": "12345678-1234-1234-1234-123456789012",
            "clientId": "12345-123456",
            "username": "user@example.com",
            "homeAccountId": "user.tenant",
            "authority": KnownAuthorities.AZURE_PUBLIC_CLOUD,
        }
        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            json.dump(invalid_data, tmp_file)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with pytest.raises(CredentialUnavailableError):
                    VisualStudioCodeCredential().get_token_info("https://management.azure.com/.default")
        finally:
            os.unlink(tmp_file.name)

    def test_broker_credential_requirements_not_installed(self):
        """Test that the credential works without the broker installed."""

        with patch.dict("sys.modules", {"azure.identity.broker": None}):
            with pytest.raises(CredentialUnavailableError):
                # This should raise an error because the broker requirements are not installed.
                VisualStudioCodeCredential().get_token_info("https://management.azure.com/.default")


class TestLoadVSCodeAuthRecord:
    """Test cases for loading VS Code authentication records."""

    def test_load_nonexistent_file(self):
        """Test loading returns None when auth record file doesn't exist."""
        with patch("os.path.exists", return_value=False):
            result = load_vscode_auth_record()
            assert result is None

    def test_load_valid_record(self):
        """Test loading a valid authentication record."""
        valid_data = {
            "tenantId": "12345678-1234-1234-1234-123456789012",
            "clientId": AZURE_VSCODE_CLIENT_ID,
            "username": "user@example.com",
            "homeAccountId": "user.tenant",
            "authority": KnownAuthorities.AZURE_PUBLIC_CLOUD,
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            json.dump(valid_data, tmp_file)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with patch("os.path.exists", return_value=True):
                    result = load_vscode_auth_record()

                    assert result is not None
                    assert isinstance(result, AuthenticationRecord)
                    assert result.tenant_id == valid_data["tenantId"]
                    assert result.client_id == valid_data["clientId"]
                    assert result.username == valid_data["username"]
                    assert result.home_account_id == valid_data["homeAccountId"]
                    assert result.authority == valid_data["authority"]
        finally:
            os.unlink(tmp_file.name)

    def test_load_malformed_json(self):
        """Test loading fails with malformed JSON."""
        malformed_json = '{"tenantId": "test", "clientId": '  # incomplete JSON

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            tmp_file.write(malformed_json)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with patch("os.path.exists", return_value=True):
                    with pytest.raises(ValueError):
                        load_vscode_auth_record()
        finally:
            os.unlink(tmp_file.name)

    def test_load_invalid_record(self):
        """Test loading fails with invalid authentication record data."""
        invalid_data = {
            "tenantId": "12345678-1234-1234-1234-123456789012",
            "clientId": "wrong-client-id",  # Invalid client ID
            "username": "user@example.com",
            "homeAccountId": "user.tenant",
            "authority": KnownAuthorities.AZURE_PUBLIC_CLOUD,
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            json.dump(invalid_data, tmp_file)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with patch("os.path.exists", return_value=True):
                    with pytest.raises(ValueError, match="Authentication record validation failed"):
                        load_vscode_auth_record()
        finally:
            os.unlink(tmp_file.name)

    def test_load_missing_required_fields(self):
        """Test loading fails when required fields are missing."""
        incomplete_data = {
            "tenantId": "12345678-1234-1234-1234-123456789012",
            "clientId": AZURE_VSCODE_CLIENT_ID,
            # Missing username, homeAccountId, authority
        }

        with tempfile.NamedTemporaryFile(mode="w", suffix=".json", delete=False) as tmp_file:
            json.dump(incomplete_data, tmp_file)
            tmp_file.flush()

        try:
            with patch("os.path.expanduser", return_value=tmp_file.name):
                with patch("os.path.exists", return_value=True):
                    with pytest.raises(ValueError) as exc_info:
                        load_vscode_auth_record()

                    error_message = str(exc_info.value)
                    assert "username field is missing" in error_message
                    assert "homeAccountId field is missing" in error_message
                    assert "authority field is missing" in error_message
        finally:
            os.unlink(tmp_file.name)