|
10 | 10 |
|
11 | 11 | ASSET_DIR = Path(__file__).parent / "assets" |
12 | 12 |
|
| 13 | +VIRTUAL_CONNECTION_ADD_TAGS = ASSET_DIR / "virtual_connection_add_tags.xml" |
13 | 14 | VIRTUAL_CONNECTION_GET_XML = ASSET_DIR / "virtual_connections_get.xml" |
| 15 | +VIRTUAL_CONNECTION_GET_WITH_TAGS_XML = ASSET_DIR / "virtual_connections_get_with_tags.xml" |
14 | 16 | VIRTUAL_CONNECTION_POPULATE_CONNECTIONS = ASSET_DIR / "virtual_connection_populate_connections.xml" |
15 | 17 | VIRTUAL_CONNECTION_POPULATE_CONNECTIONS2 = ASSET_DIR / "virtual_connection_populate_connections2.xml" |
16 | 18 | VC_DB_CONN_UPDATE = ASSET_DIR / "virtual_connection_database_connection_update.xml" |
@@ -46,6 +48,33 @@ def test_from_xml(server: TSC.Server) -> None: |
46 | 48 | assert virtual_connection.name == "vconn" |
47 | 49 | assert virtual_connection.updated_at == parse_datetime("2024-06-18T09:00:00Z") |
48 | 50 | assert virtual_connection.webpage_url == "https://test/#/site/site-name/virtualconnections/3" |
| 51 | + # Response element carries <tags> since Tableau Server 2026.2 (API 3.30); |
| 52 | + # empty when the VC has no tags. |
| 53 | + assert virtual_connection.tags == set() |
| 54 | + assert virtual_connection._initial_tags == set() |
| 55 | + |
| 56 | + |
| 57 | +def test_from_xml_populated_tags(server: TSC.Server) -> None: |
| 58 | + """When the response's <tags> element carries <tag label="..."/> children, |
| 59 | + those values populate `tags` and `_initial_tags` so the diff-based |
| 60 | + update_tags mixin can compute changes. |
| 61 | + """ |
| 62 | + items = VirtualConnectionItem.from_response(VIRTUAL_CONNECTION_GET_WITH_TAGS_XML.read_bytes(), server.namespace) |
| 63 | + |
| 64 | + assert len(items) == 2 |
| 65 | + |
| 66 | + with_tags = items[0] |
| 67 | + assert with_tags.name == "vconn_with_tags" |
| 68 | + assert with_tags.tags == {"finance", "certified"} |
| 69 | + assert with_tags._initial_tags == {"finance", "certified"} |
| 70 | + # _initial_tags is a copy: mutating tags after parse doesn't leak back |
| 71 | + with_tags.tags.add("later-added") |
| 72 | + assert with_tags._initial_tags == {"finance", "certified"} |
| 73 | + |
| 74 | + without_tags = items[1] |
| 75 | + assert without_tags.name == "vconn_no_tags" |
| 76 | + assert without_tags.tags == set() |
| 77 | + assert without_tags._initial_tags == set() |
49 | 78 |
|
50 | 79 |
|
51 | 80 | def test_virtual_connection_get(server: TSC.Server) -> None: |
@@ -113,7 +142,10 @@ def test_virtual_connection_get_by_id(server: TSC.Server) -> None: |
113 | 142 |
|
114 | 143 | assert vconn.content |
115 | 144 | assert vconn.created_at is None |
116 | | - assert vconn.id is None |
| 145 | + # The server's Get Virtual Connection response omits the `id` attribute on |
| 146 | + # the <virtualConnection> element, but get_by_id stamps it back from the |
| 147 | + # request path so downstream tag/permission calls can find it. |
| 148 | + assert vconn.id == "8fd7cc02-bb55-4d15-b8b1-9650239efe79" |
117 | 149 | assert "policyCollection" in vconn.content |
118 | 150 | assert "revision" in vconn.content |
119 | 151 |
|
@@ -240,6 +272,32 @@ def test_virtual_connection_publish_draft_overwrite(server: TSC.Server) -> None: |
240 | 272 | assert "revision" in vconn.content |
241 | 273 |
|
242 | 274 |
|
| 275 | +def test_update_tags_diff_round_trip(server: TSC.Server) -> None: |
| 276 | + """Diff-based update_tags computes add-set / remove-set from the item's |
| 277 | + tags and _initial_tags and issues the right PUT / DELETE calls. |
| 278 | +
|
| 279 | + Simulates: server returned {a,b,c,d} at fetch time; caller mutated to |
| 280 | + {a,c,e}. Expected calls: PUT /tags with {a,c,e} (add path adds e), DELETE |
| 281 | + /tags/b, DELETE /tags/d. |
| 282 | + """ |
| 283 | + server.version = "3.30" # update_tags requires 3.30 (see @api decorator) |
| 284 | + vconn_id = "8fd7cc02-bb55-4d15-b8b1-9650239efe79" |
| 285 | + add_tags_response = VIRTUAL_CONNECTION_ADD_TAGS.read_text() |
| 286 | + with requests_mock.mock() as m: |
| 287 | + m.put(f"{server.virtual_connections.baseurl}/{vconn_id}/tags", text=add_tags_response) |
| 288 | + m.delete(f"{server.virtual_connections.baseurl}/{vconn_id}/tags/b", status_code=204) |
| 289 | + m.delete(f"{server.virtual_connections.baseurl}/{vconn_id}/tags/d", status_code=204) |
| 290 | + |
| 291 | + vconn = VirtualConnectionItem("vconn") |
| 292 | + vconn._id = vconn_id |
| 293 | + vconn._initial_tags = {"a", "b", "c", "d"} |
| 294 | + vconn.tags = {"a", "c", "e"} |
| 295 | + server.virtual_connections.update_tags(vconn) |
| 296 | + |
| 297 | + # add PUT + 2 deletes = 3 calls |
| 298 | + assert m.call_count == 3, m.request_history |
| 299 | + |
| 300 | + |
243 | 301 | def test_add_permissions(server: TSC.Server) -> None: |
244 | 302 | response_xml = ADD_PERMISSIONS.read_text() |
245 | 303 |
|
|
0 commit comments