|
1 | | -# Copyright (c) Microsoft. All rights reserved. |
2 | | - |
3 | | -import unittest |
4 | | - |
5 | | -from microsoft_agents_a365.runtime.power_platform_api_discovery import PowerPlatformApiDiscovery |
6 | | - |
7 | | - |
8 | | -class TestPowerPlatformApiDiscovery(unittest.TestCase): |
9 | | - def test_host_suffix_and_audience(self): |
10 | | - expected_host_suffixes = { |
11 | | - "local": "api.powerplatform.localhost", |
12 | | - "dev": "api.powerplatform.com", |
13 | | - "test": "api.powerplatform.com", |
14 | | - "preprod": "api.powerplatform.com", |
15 | | - "firstrelease": "api.powerplatform.com", |
16 | | - "prod": "api.powerplatform.com", |
17 | | - "gov": "api.gov.powerplatform.microsoft.us", |
18 | | - "high": "api.high.powerplatform.microsoft.us", |
19 | | - "dod": "api.appsplatform.us", |
20 | | - "mooncake": "api.powerplatform.partner.microsoftonline.cn", |
21 | | - "ex": "api.powerplatform.eaglex.ic.gov", |
22 | | - "rx": "api.powerplatform.microsoft.scloud", |
23 | | - } |
24 | | - |
25 | | - for cluster, expected in expected_host_suffixes.items(): |
26 | | - with self.subTest(cluster=cluster): |
27 | | - disc = PowerPlatformApiDiscovery(cluster) # type: ignore[arg-type] |
28 | | - self.assertEqual(disc.get_token_endpoint_host(), expected) |
29 | | - self.assertEqual(disc.get_token_audience(), f"https://{expected}") |
30 | | - |
31 | | - def test_hex_suffix_length_rules(self): |
32 | | - prod = PowerPlatformApiDiscovery("prod") |
33 | | - first = PowerPlatformApiDiscovery("firstrelease") |
34 | | - dev = PowerPlatformApiDiscovery("dev") |
35 | | - |
36 | | - self.assertEqual(prod._get_hex_api_suffix_length(), 2) |
37 | | - self.assertEqual(first._get_hex_api_suffix_length(), 2) |
38 | | - self.assertEqual(dev._get_hex_api_suffix_length(), 1) |
39 | | - |
40 | | - def test_tenant_endpoint_generation_prod(self): |
41 | | - disc = PowerPlatformApiDiscovery("prod") |
42 | | - tenant_id = "abc-012" # normalized -> abc012; suffix length 2 -> '12' |
43 | | - expected = "abc0.12.tenant.api.powerplatform.com" |
44 | | - self.assertEqual(disc.get_tenant_endpoint(tenant_id), expected) |
45 | | - |
46 | | - def test_tenant_endpoint_generation_dev(self): |
47 | | - disc = PowerPlatformApiDiscovery("dev") |
48 | | - tenant_id = "A1B2" # normalized -> a1b2; suffix length 1 -> '2' |
49 | | - expected = "a1b.2.tenant.api.powerplatform.com" |
50 | | - self.assertEqual(disc.get_tenant_endpoint(tenant_id), expected) |
51 | | - |
52 | | - def test_tenant_island_cluster_endpoint(self): |
53 | | - disc = PowerPlatformApiDiscovery("prod") |
54 | | - tenant_id = "abc-1234" # normalized -> abc1234; suffix '34', prefix 'abc12' |
55 | | - expected = "il-abc12.34.tenant.api.powerplatform.com" |
56 | | - self.assertEqual(disc.get_tenant_island_cluster_endpoint(tenant_id), expected) |
57 | | - |
58 | | - def test_invalid_characters_in_tenant_identifier(self): |
59 | | - disc = PowerPlatformApiDiscovery("dev") |
60 | | - with self.assertRaisesRegex(ValueError, r"invalid host name characters"): |
61 | | - disc.get_tenant_endpoint("invalid$name") |
62 | | - |
63 | | - def test_tenant_identifier_too_short_for_suffix(self): |
64 | | - disc = PowerPlatformApiDiscovery("prod") |
65 | | - # prod requires normalized length >= 3 (2 + 1). Provide only 2 characters. |
66 | | - with self.assertRaisesRegex(ValueError, r"must be at least"): |
67 | | - disc.get_tenant_endpoint("ab") |
68 | | - |
69 | | - def test_normalization_of_tenant_id(self): |
70 | | - disc = PowerPlatformApiDiscovery("dev") |
71 | | - tenant_id = "Ab-Cd-Ef" # normalized -> abcdef; suffix 1 -> 'f', prefix 'abcde' |
72 | | - expected = "abcde.f.tenant.api.powerplatform.com" |
73 | | - self.assertEqual(disc.get_tenant_endpoint(tenant_id), expected) |
74 | | - |
75 | | - def test_nodejs_tenant_examples(self): |
76 | | - tenant_id = "e3064512-cc6d-4703-be71-a2ecaecaa98a" |
77 | | - expected_map = { |
78 | | - "local": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost", |
79 | | - "dev": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
80 | | - "test": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
81 | | - "preprod": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
82 | | - "firstrelease": "e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com", |
83 | | - "prod": "e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com", |
84 | | - "gov": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.gov.powerplatform.microsoft.us", |
85 | | - "high": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.high.powerplatform.microsoft.us", |
86 | | - "dod": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.appsplatform.us", |
87 | | - "mooncake": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.partner.microsoftonline.cn", |
88 | | - "ex": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.eaglex.ic.gov", |
89 | | - "rx": "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.microsoft.scloud", |
90 | | - } |
91 | | - |
92 | | - for cluster, expected in expected_map.items(): |
93 | | - with self.subTest(cluster=cluster): |
94 | | - disc = PowerPlatformApiDiscovery(cluster) # type: ignore[arg-type] |
95 | | - self.assertEqual(disc.get_tenant_endpoint(tenant_id), expected) |
96 | | - |
97 | | - def test_nodejs_tenant_island_examples(self): |
98 | | - tenant_id = "e3064512-cc6d-4703-be71-a2ecaecaa98a" |
99 | | - expected_map = { |
100 | | - "local": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost", |
101 | | - "dev": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
102 | | - "test": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
103 | | - "preprod": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com", |
104 | | - "firstrelease": "il-e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com", |
105 | | - "prod": "il-e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com", |
106 | | - "gov": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.gov.powerplatform.microsoft.us", |
107 | | - "high": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.high.powerplatform.microsoft.us", |
108 | | - "dod": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.appsplatform.us", |
109 | | - "mooncake": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.partner.microsoftonline.cn", |
110 | | - "ex": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.eaglex.ic.gov", |
111 | | - "rx": "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.microsoft.scloud", |
112 | | - } |
113 | | - |
114 | | - for cluster, expected in expected_map.items(): |
115 | | - with self.subTest(cluster=cluster): |
116 | | - disc = PowerPlatformApiDiscovery(cluster) # type: ignore[arg-type] |
117 | | - self.assertEqual(disc.get_tenant_island_cluster_endpoint(tenant_id), expected) |
118 | | - |
119 | | - def test_nodejs_invalid_characters_exact_message(self): |
120 | | - disc = PowerPlatformApiDiscovery("local") |
121 | | - expected_msg = "Cannot generate Power Platform API endpoint because the tenant identifier contains invalid host name characters, only alphanumeric and dash characters are expected: invalid?" |
122 | | - with self.assertRaises(ValueError) as cm: |
123 | | - disc.get_tenant_endpoint("invalid?") |
124 | | - self.assertEqual(str(cm.exception), expected_msg) |
125 | | - |
126 | | - def test_nodejs_insufficient_length_exact_messages(self): |
127 | | - disc_local = PowerPlatformApiDiscovery("local") |
128 | | - with self.assertRaises(ValueError) as cm1: |
129 | | - disc_local.get_tenant_endpoint("a") |
130 | | - self.assertEqual( |
131 | | - str(cm1.exception), |
132 | | - "Cannot generate Power Platform API endpoint because the normalized tenant identifier must be at least 2 characters in length: a", |
133 | | - ) |
134 | | - |
135 | | - with self.assertRaises(ValueError) as cm2: |
136 | | - disc_local.get_tenant_endpoint("a-") |
137 | | - self.assertEqual( |
138 | | - str(cm2.exception), |
139 | | - "Cannot generate Power Platform API endpoint because the normalized tenant identifier must be at least 2 characters in length: a", |
140 | | - ) |
141 | | - |
142 | | - disc_prod = PowerPlatformApiDiscovery("prod") |
143 | | - with self.assertRaises(ValueError) as cm3: |
144 | | - disc_prod.get_tenant_endpoint("aa") |
145 | | - self.assertEqual( |
146 | | - str(cm3.exception), |
147 | | - "Cannot generate Power Platform API endpoint because the normalized tenant identifier must be at least 3 characters in length: aa", |
148 | | - ) |
149 | | - |
150 | | - with self.assertRaises(ValueError) as cm4: |
151 | | - disc_prod.get_tenant_endpoint("a-a") |
152 | | - self.assertEqual( |
153 | | - str(cm4.exception), |
154 | | - "Cannot generate Power Platform API endpoint because the normalized tenant identifier must be at least 3 characters in length: aa", |
155 | | - ) |
156 | | - |
157 | | - |
158 | | -if __name__ == "__main__": |
159 | | - unittest.main() |
| 1 | +# Copyright (c) Microsoft Corporation. |
| 2 | +# Licensed under the MIT License. |
| 3 | + |
| 4 | +"""Unit tests for PowerPlatformApiDiscovery class.""" |
| 5 | + |
| 6 | +import re |
| 7 | +import pytest |
| 8 | + |
| 9 | +from microsoft_agents_a365.runtime.power_platform_api_discovery import ( |
| 10 | + PowerPlatformApiDiscovery, |
| 11 | +) |
| 12 | + |
| 13 | + |
| 14 | +# Tests for get_token_endpoint_host and get_token_audience |
| 15 | +@pytest.mark.parametrize( |
| 16 | + "cluster,expected_host", |
| 17 | + [ |
| 18 | + ("local", "api.powerplatform.localhost"), |
| 19 | + ("dev", "api.powerplatform.com"), |
| 20 | + ("test", "api.powerplatform.com"), |
| 21 | + ("preprod", "api.powerplatform.com"), |
| 22 | + ("firstrelease", "api.powerplatform.com"), |
| 23 | + ("prod", "api.powerplatform.com"), |
| 24 | + ("gov", "api.gov.powerplatform.microsoft.us"), |
| 25 | + ("high", "api.high.powerplatform.microsoft.us"), |
| 26 | + ("dod", "api.appsplatform.us"), |
| 27 | + ("mooncake", "api.powerplatform.partner.microsoftonline.cn"), |
| 28 | + ("ex", "api.powerplatform.eaglex.ic.gov"), |
| 29 | + ("rx", "api.powerplatform.microsoft.scloud"), |
| 30 | + ], |
| 31 | +) |
| 32 | +def test_host_suffix_and_audience(cluster, expected_host): |
| 33 | + """Test get_token_endpoint_host and get_token_audience return correct values for each cluster.""" |
| 34 | + disc = PowerPlatformApiDiscovery(cluster) |
| 35 | + assert disc.get_token_endpoint_host() == expected_host |
| 36 | + assert disc.get_token_audience() == f"https://{expected_host}" |
| 37 | + |
| 38 | + |
| 39 | +# Tests for _get_hex_api_suffix_length |
| 40 | +@pytest.mark.parametrize( |
| 41 | + "cluster,expected_length", |
| 42 | + [ |
| 43 | + ("prod", 2), |
| 44 | + ("firstrelease", 2), |
| 45 | + ("dev", 1), |
| 46 | + ("test", 1), |
| 47 | + ("preprod", 1), |
| 48 | + ("local", 1), |
| 49 | + ("gov", 1), |
| 50 | + ("high", 1), |
| 51 | + ("dod", 1), |
| 52 | + ("mooncake", 1), |
| 53 | + ("ex", 1), |
| 54 | + ("rx", 1), |
| 55 | + ], |
| 56 | +) |
| 57 | +def test_hex_suffix_length_rules(cluster, expected_length): |
| 58 | + """Test _get_hex_api_suffix_length returns correct suffix length for each cluster.""" |
| 59 | + disc = PowerPlatformApiDiscovery(cluster) |
| 60 | + assert disc._get_hex_api_suffix_length() == expected_length |
| 61 | + |
| 62 | + |
| 63 | +# Tests for get_tenant_endpoint |
| 64 | +@pytest.mark.parametrize( |
| 65 | + "cluster,tenant_id,expected", |
| 66 | + [ |
| 67 | + ("prod", "abc-012", "abc0.12.tenant.api.powerplatform.com"), |
| 68 | + ("dev", "A1B2", "a1b.2.tenant.api.powerplatform.com"), |
| 69 | + ("dev", "Ab-Cd-Ef", "abcde.f.tenant.api.powerplatform.com"), |
| 70 | + ], |
| 71 | +) |
| 72 | +def test_tenant_endpoint_generation(cluster, tenant_id, expected): |
| 73 | + """Test get_tenant_endpoint for various clusters and tenant IDs.""" |
| 74 | + disc = PowerPlatformApiDiscovery(cluster) |
| 75 | + assert disc.get_tenant_endpoint(tenant_id) == expected |
| 76 | + |
| 77 | + |
| 78 | +@pytest.mark.parametrize( |
| 79 | + "cluster,expected", |
| 80 | + [ |
| 81 | + ("local", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost"), |
| 82 | + ("dev", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 83 | + ("test", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 84 | + ("preprod", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 85 | + ("firstrelease", "e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com"), |
| 86 | + ("prod", "e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com"), |
| 87 | + ("gov", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.gov.powerplatform.microsoft.us"), |
| 88 | + ("high", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.high.powerplatform.microsoft.us"), |
| 89 | + ("dod", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.appsplatform.us"), |
| 90 | + ( |
| 91 | + "mooncake", |
| 92 | + "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.partner.microsoftonline.cn", |
| 93 | + ), |
| 94 | + ("ex", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.eaglex.ic.gov"), |
| 95 | + ("rx", "e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.microsoft.scloud"), |
| 96 | + ], |
| 97 | +) |
| 98 | +def test_tenant_endpoint_with_real_uuid(cluster, expected): |
| 99 | + """Test get_tenant_endpoint with real UUID across all clusters.""" |
| 100 | + tenant_id = "e3064512-cc6d-4703-be71-a2ecaecaa98a" |
| 101 | + disc = PowerPlatformApiDiscovery(cluster) |
| 102 | + assert disc.get_tenant_endpoint(tenant_id) == expected |
| 103 | + |
| 104 | + |
| 105 | +# Tests for get_tenant_island_cluster_endpoint |
| 106 | +@pytest.mark.parametrize( |
| 107 | + "cluster,expected", |
| 108 | + [ |
| 109 | + ("local", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.localhost"), |
| 110 | + ("dev", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 111 | + ("test", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 112 | + ("preprod", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.com"), |
| 113 | + ("firstrelease", "il-e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com"), |
| 114 | + ("prod", "il-e3064512cc6d4703be71a2ecaecaa9.8a.tenant.api.powerplatform.com"), |
| 115 | + ("gov", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.gov.powerplatform.microsoft.us"), |
| 116 | + ("high", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.high.powerplatform.microsoft.us"), |
| 117 | + ("dod", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.appsplatform.us"), |
| 118 | + ( |
| 119 | + "mooncake", |
| 120 | + "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.partner.microsoftonline.cn", |
| 121 | + ), |
| 122 | + ("ex", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.eaglex.ic.gov"), |
| 123 | + ("rx", "il-e3064512cc6d4703be71a2ecaecaa98.a.tenant.api.powerplatform.microsoft.scloud"), |
| 124 | + ], |
| 125 | +) |
| 126 | +def test_tenant_island_cluster_endpoint(cluster, expected): |
| 127 | + """Test get_tenant_island_cluster_endpoint with real UUID across all clusters.""" |
| 128 | + tenant_id = "e3064512-cc6d-4703-be71-a2ecaecaa98a" |
| 129 | + disc = PowerPlatformApiDiscovery(cluster) |
| 130 | + assert disc.get_tenant_island_cluster_endpoint(tenant_id) == expected |
| 131 | + |
| 132 | + |
| 133 | +# Tests for error handling |
| 134 | +@pytest.mark.parametrize( |
| 135 | + "tenant_id", |
| 136 | + [ |
| 137 | + "invalid$name", |
| 138 | + "invalid?", |
| 139 | + "tenant@id", |
| 140 | + "tenant#123", |
| 141 | + "tenant with spaces", |
| 142 | + "", |
| 143 | + "---", |
| 144 | + "-", |
| 145 | + ], |
| 146 | +) |
| 147 | +def test_invalid_tenant_identifier(tenant_id): |
| 148 | + """Test ValueError is raised for invalid tenant IDs (invalid chars, empty, or all dashes).""" |
| 149 | + disc = PowerPlatformApiDiscovery("dev") |
| 150 | + with pytest.raises(ValueError): |
| 151 | + disc.get_tenant_endpoint(tenant_id) |
| 152 | + |
| 153 | + |
| 154 | +@pytest.mark.parametrize( |
| 155 | + "cluster,tenant_id,min_length", |
| 156 | + [ |
| 157 | + ("local", "a", 2), |
| 158 | + ("local", "a-", 2), |
| 159 | + ("prod", "aa", 3), |
| 160 | + ("prod", "a-a", 3), |
| 161 | + ], |
| 162 | +) |
| 163 | +def test_tenant_identifier_too_short(cluster, tenant_id, min_length): |
| 164 | + """Test ValueError is raised when tenant ID is too short after normalization.""" |
| 165 | + disc = PowerPlatformApiDiscovery(cluster) |
| 166 | + with pytest.raises(ValueError, match=f"must be at least {min_length}"): |
| 167 | + disc.get_tenant_endpoint(tenant_id) |
0 commit comments