|
1 | | -import os |
2 | | -import unittest |
3 | 1 | from datetime import datetime |
| 2 | +from pathlib import Path |
4 | 3 |
|
| 4 | +import pytest |
5 | 5 | import requests_mock |
6 | 6 |
|
7 | 7 | import tableauserverclient as TSC |
8 | 8 | from tableauserverclient.datetime_helpers import utc |
9 | 9 | from tableauserverclient.server.endpoint.exceptions import JobFailedException |
10 | | -from ._utils import read_xml_asset, mocked_time |
11 | | - |
12 | | -GET_XML = "job_get.xml" |
13 | | -GET_BY_ID_XML = "job_get_by_id.xml" |
14 | | -GET_BY_ID_COMPLETED_XML = "job_get_by_id_completed.xml" |
15 | | -GET_BY_ID_FAILED_XML = "job_get_by_id_failed.xml" |
16 | | -GET_BY_ID_CANCELLED_XML = "job_get_by_id_cancelled.xml" |
17 | | -GET_BY_ID_INPROGRESS_XML = "job_get_by_id_inprogress.xml" |
18 | | -GET_BY_ID_WORKBOOK = "job_get_by_id_failed_workbook.xml" |
19 | | - |
20 | | - |
21 | | -class JobTests(unittest.TestCase): |
22 | | - def setUp(self) -> None: |
23 | | - self.server = TSC.Server("http://test", False) |
24 | | - self.server.version = "3.1" |
25 | | - |
26 | | - # Fake signin |
27 | | - self.server._site_id = "dad65087-b08b-4603-af4e-2887b8aafc67" |
28 | | - self.server._auth_token = "j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM" |
29 | | - |
30 | | - self.baseurl = self.server.jobs.baseurl |
31 | | - |
32 | | - def test_get(self) -> None: |
33 | | - response_xml = read_xml_asset(GET_XML) |
34 | | - with requests_mock.mock() as m: |
35 | | - m.get(self.baseurl, text=response_xml) |
36 | | - all_jobs, pagination_item = self.server.jobs.get() |
37 | | - job = all_jobs[0] |
38 | | - created_at = datetime(2018, 5, 22, 13, 0, 29, tzinfo=utc) |
39 | | - started_at = datetime(2018, 5, 22, 13, 0, 37, tzinfo=utc) |
40 | | - ended_at = datetime(2018, 5, 22, 13, 0, 45, tzinfo=utc) |
41 | | - |
42 | | - self.assertEqual(1, pagination_item.total_available) |
43 | | - self.assertEqual("2eef4225-aa0c-41c4-8662-a76d89ed7336", job.id) |
44 | | - self.assertEqual("Success", job.status) |
45 | | - self.assertEqual("50", job.priority) |
46 | | - self.assertEqual("single_subscription_notify", job.type) |
47 | | - self.assertEqual(created_at, job.created_at) |
48 | | - self.assertEqual(started_at, job.started_at) |
49 | | - self.assertEqual(ended_at, job.ended_at) |
50 | | - |
51 | | - def test_get_by_id(self) -> None: |
52 | | - response_xml = read_xml_asset(GET_BY_ID_XML) |
53 | | - job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
54 | | - with requests_mock.mock() as m: |
55 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
56 | | - job = self.server.jobs.get_by_id(job_id) |
57 | | - updated_at = datetime(2020, 5, 13, 20, 25, 18, tzinfo=utc) |
58 | | - |
59 | | - self.assertEqual(job_id, job.id) |
60 | | - self.assertEqual(updated_at, job.updated_at) |
61 | | - self.assertListEqual(job.notes, ["Job detail notes"]) |
62 | | - |
63 | | - def test_get_before_signin(self) -> None: |
64 | | - self.server._auth_token = None |
65 | | - self.assertRaises(TSC.NotSignedInError, self.server.jobs.get) |
66 | | - |
67 | | - def test_cancel_id(self) -> None: |
68 | | - with requests_mock.mock() as m: |
69 | | - m.put(self.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", status_code=204) |
70 | | - self.server.jobs.cancel("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") |
71 | | - |
72 | | - def test_cancel_item(self) -> None: |
| 10 | +from ._utils import mocked_time |
| 11 | + |
| 12 | + |
| 13 | +TEST_ASSET_DIR = Path(__file__).parent / "assets" |
| 14 | +GET_XML = TEST_ASSET_DIR / "job_get.xml" |
| 15 | +GET_BY_ID_XML = TEST_ASSET_DIR / "job_get_by_id.xml" |
| 16 | +GET_BY_ID_COMPLETED_XML = TEST_ASSET_DIR / "job_get_by_id_completed.xml" |
| 17 | +GET_BY_ID_FAILED_XML = TEST_ASSET_DIR / "job_get_by_id_failed.xml" |
| 18 | +GET_BY_ID_CANCELLED_XML = TEST_ASSET_DIR / "job_get_by_id_cancelled.xml" |
| 19 | +GET_BY_ID_INPROGRESS_XML = TEST_ASSET_DIR / "job_get_by_id_inprogress.xml" |
| 20 | +GET_BY_ID_WORKBOOK = TEST_ASSET_DIR / "job_get_by_id_failed_workbook.xml" |
| 21 | + |
| 22 | + |
| 23 | +@pytest.fixture(scope="function") |
| 24 | +def server(): |
| 25 | + """Fixture to create a TSC.Server instance for testing.""" |
| 26 | + server = TSC.Server("http://test", False) |
| 27 | + |
| 28 | + # Fake signin |
| 29 | + server._site_id = "dad65087-b08b-4603-af4e-2887b8aafc67" |
| 30 | + server._auth_token = "j80k54ll2lfMZ0tv97mlPvvSCRyD0DOM" |
| 31 | + server.version = "3.1" |
| 32 | + |
| 33 | + return server |
| 34 | + |
| 35 | + |
| 36 | +def test_get(server: TSC.Server) -> None: |
| 37 | + response_xml = GET_XML.read_text() |
| 38 | + with requests_mock.mock() as m: |
| 39 | + m.get(server.jobs.baseurl, text=response_xml) |
| 40 | + all_jobs, pagination_item = server.jobs.get() |
| 41 | + job = all_jobs[0] |
73 | 42 | created_at = datetime(2018, 5, 22, 13, 0, 29, tzinfo=utc) |
74 | 43 | started_at = datetime(2018, 5, 22, 13, 0, 37, tzinfo=utc) |
75 | | - job = TSC.JobItem("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", "backgroundJob", "0", created_at, started_at, None, 0) |
76 | | - with requests_mock.mock() as m: |
77 | | - m.put(self.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", status_code=204) |
78 | | - self.server.jobs.cancel(job) |
79 | | - |
80 | | - def test_wait_for_job_finished(self) -> None: |
81 | | - # Waiting for an already finished job, directly returns that job's info |
82 | | - response_xml = read_xml_asset(GET_BY_ID_XML) |
83 | | - job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
84 | | - with mocked_time(), requests_mock.mock() as m: |
85 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
86 | | - job = self.server.jobs.wait_for_job(job_id) |
87 | | - |
88 | | - self.assertEqual(job_id, job.id) |
89 | | - self.assertListEqual(job.notes, ["Job detail notes"]) |
90 | | - |
91 | | - def test_wait_for_job_completed(self) -> None: |
92 | | - # Waiting for a bridge (cloud) job completion |
93 | | - response_xml = read_xml_asset(GET_BY_ID_COMPLETED_XML) |
94 | | - job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
95 | | - with mocked_time(), requests_mock.mock() as m: |
96 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
97 | | - job = self.server.jobs.wait_for_job(job_id) |
98 | | - |
99 | | - self.assertEqual(job_id, job.id) |
100 | | - self.assertListEqual(job.notes, ["Job detail notes"]) |
101 | | - |
102 | | - def test_wait_for_job_failed(self) -> None: |
103 | | - # Waiting for a failed job raises an exception |
104 | | - response_xml = read_xml_asset(GET_BY_ID_FAILED_XML) |
105 | | - job_id = "77d5e57a-2517-479f-9a3c-a32025f2b64d" |
106 | | - with mocked_time(), requests_mock.mock() as m: |
107 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
108 | | - with self.assertRaises(JobFailedException): |
109 | | - self.server.jobs.wait_for_job(job_id) |
110 | | - |
111 | | - def test_wait_for_job_timeout(self) -> None: |
112 | | - # Waiting for a job which doesn't terminate will throw an exception |
113 | | - response_xml = read_xml_asset(GET_BY_ID_INPROGRESS_XML) |
114 | | - job_id = "77d5e57a-2517-479f-9a3c-a32025f2b64d" |
115 | | - with mocked_time(), requests_mock.mock() as m: |
116 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
117 | | - with self.assertRaises(TimeoutError): |
118 | | - self.server.jobs.wait_for_job(job_id, timeout=30) |
119 | | - |
120 | | - def test_get_job_datasource_id(self) -> None: |
121 | | - response_xml = read_xml_asset(GET_BY_ID_FAILED_XML) |
122 | | - job_id = "777bf7c4-421d-4b2c-a518-11b90187c545" |
123 | | - with requests_mock.mock() as m: |
124 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
125 | | - job = self.server.jobs.get_by_id(job_id) |
126 | | - self.assertEqual(job.datasource_id, "03b9fbec-81f6-4160-ae49-5f9f6d412758") |
127 | | - |
128 | | - def test_get_job_workbook_id(self) -> None: |
129 | | - response_xml = read_xml_asset(GET_BY_ID_WORKBOOK) |
130 | | - job_id = "bb1aab79-db54-4e96-9dd3-461d8f081d08" |
131 | | - with requests_mock.mock() as m: |
132 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
133 | | - job = self.server.jobs.get_by_id(job_id) |
134 | | - self.assertEqual(job.workbook_id, "5998aaaf-1abe-4d38-b4d9-bc53e85bdd13") |
135 | | - |
136 | | - def test_get_job_workbook_name(self) -> None: |
137 | | - response_xml = read_xml_asset(GET_BY_ID_WORKBOOK) |
138 | | - job_id = "bb1aab79-db54-4e96-9dd3-461d8f081d08" |
139 | | - with requests_mock.mock() as m: |
140 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
141 | | - job = self.server.jobs.get_by_id(job_id) |
142 | | - self.assertEqual(job.workbook_name, "Superstore") |
143 | | - |
144 | | - def test_get_job_datasource_name(self) -> None: |
145 | | - response_xml = read_xml_asset(GET_BY_ID_FAILED_XML) |
146 | | - job_id = "777bf7c4-421d-4b2c-a518-11b90187c545" |
147 | | - with requests_mock.mock() as m: |
148 | | - m.get(f"{self.baseurl}/{job_id}", text=response_xml) |
149 | | - job = self.server.jobs.get_by_id(job_id) |
150 | | - self.assertEqual(job.datasource_name, "World Indicators") |
151 | | - |
152 | | - def test_background_job_str(self) -> None: |
153 | | - job = TSC.BackgroundJobItem( |
154 | | - "ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", datetime.now(), 1, "extractRefresh", "Failed" |
155 | | - ) |
156 | | - assert not str(job).startswith("<<property") |
157 | | - assert not repr(job).startswith("<<property") |
158 | | - assert "BackgroundJobItem" in str(job) |
| 44 | + ended_at = datetime(2018, 5, 22, 13, 0, 45, tzinfo=utc) |
| 45 | + |
| 46 | + assert 1 == pagination_item.total_available |
| 47 | + assert "2eef4225-aa0c-41c4-8662-a76d89ed7336" == job.id |
| 48 | + assert "Success" == job.status |
| 49 | + assert "50" == job.priority |
| 50 | + assert "single_subscription_notify" == job.type |
| 51 | + assert created_at == job.created_at |
| 52 | + assert started_at == job.started_at |
| 53 | + assert ended_at == job.ended_at |
| 54 | + |
| 55 | + |
| 56 | +def test_get_by_id(server: TSC.Server) -> None: |
| 57 | + response_xml = GET_BY_ID_XML.read_text() |
| 58 | + job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
| 59 | + with requests_mock.mock() as m: |
| 60 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 61 | + job = server.jobs.get_by_id(job_id) |
| 62 | + updated_at = datetime(2020, 5, 13, 20, 25, 18, tzinfo=utc) |
| 63 | + |
| 64 | + assert job_id == job.id |
| 65 | + assert updated_at == job.updated_at |
| 66 | + assert job.notes == ["Job detail notes"] |
| 67 | + |
| 68 | + |
| 69 | +def test_get_before_signin(server: TSC.Server) -> None: |
| 70 | + server._auth_token = None |
| 71 | + with pytest.raises(TSC.NotSignedInError): |
| 72 | + server.jobs.get() |
| 73 | + |
| 74 | + |
| 75 | +def test_cancel_id(server: TSC.Server) -> None: |
| 76 | + with requests_mock.mock() as m: |
| 77 | + m.put(server.jobs.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", status_code=204) |
| 78 | + server.jobs.cancel("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760") |
| 79 | + |
| 80 | + |
| 81 | +def test_cancel_item(server: TSC.Server) -> None: |
| 82 | + created_at = datetime(2018, 5, 22, 13, 0, 29, tzinfo=utc) |
| 83 | + started_at = datetime(2018, 5, 22, 13, 0, 37, tzinfo=utc) |
| 84 | + job = TSC.JobItem("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", "backgroundJob", "0", created_at, started_at, None, 0) |
| 85 | + with requests_mock.mock() as m: |
| 86 | + m.put(server.jobs.baseurl + "/ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", status_code=204) |
| 87 | + server.jobs.cancel(job) |
| 88 | + |
| 89 | + |
| 90 | +def test_wait_for_job_finished(server: TSC.Server) -> None: |
| 91 | + # Waiting for an already finished job, directly returns that job's info |
| 92 | + response_xml = GET_BY_ID_XML.read_text() |
| 93 | + job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
| 94 | + with mocked_time(), requests_mock.mock() as m: |
| 95 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 96 | + job = server.jobs.wait_for_job(job_id) |
| 97 | + |
| 98 | + assert job_id == job.id |
| 99 | + assert job.notes == ["Job detail notes"] |
| 100 | + |
| 101 | + |
| 102 | +def test_wait_for_job_completed(server: TSC.Server) -> None: |
| 103 | + # Waiting for a bridge (cloud) job completion |
| 104 | + response_xml = GET_BY_ID_COMPLETED_XML.read_text() |
| 105 | + job_id = "2eef4225-aa0c-41c4-8662-a76d89ed7336" |
| 106 | + with mocked_time(), requests_mock.mock() as m: |
| 107 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 108 | + job = server.jobs.wait_for_job(job_id) |
| 109 | + |
| 110 | + assert job_id == job.id |
| 111 | + assert job.notes == ["Job detail notes"] |
| 112 | + |
| 113 | + |
| 114 | +def test_wait_for_job_failed(server: TSC.Server) -> None: |
| 115 | + # Waiting for a failed job raises an exception |
| 116 | + response_xml = GET_BY_ID_FAILED_XML.read_text() |
| 117 | + job_id = "77d5e57a-2517-479f-9a3c-a32025f2b64d" |
| 118 | + with mocked_time(), requests_mock.mock() as m: |
| 119 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 120 | + with pytest.raises(JobFailedException): |
| 121 | + server.jobs.wait_for_job(job_id) |
| 122 | + |
| 123 | + |
| 124 | +def test_wait_for_job_timeout(server: TSC.Server) -> None: |
| 125 | + # Waiting for a job which doesn't terminate will throw an exception |
| 126 | + response_xml = GET_BY_ID_INPROGRESS_XML.read_text() |
| 127 | + job_id = "77d5e57a-2517-479f-9a3c-a32025f2b64d" |
| 128 | + with mocked_time(), requests_mock.mock() as m: |
| 129 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 130 | + with pytest.raises(TimeoutError): |
| 131 | + server.jobs.wait_for_job(job_id, timeout=30) |
| 132 | + |
| 133 | + |
| 134 | +def test_get_job_datasource_id(server: TSC.Server) -> None: |
| 135 | + response_xml = GET_BY_ID_FAILED_XML.read_text() |
| 136 | + job_id = "777bf7c4-421d-4b2c-a518-11b90187c545" |
| 137 | + with requests_mock.mock() as m: |
| 138 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 139 | + job = server.jobs.get_by_id(job_id) |
| 140 | + assert job.datasource_id == "03b9fbec-81f6-4160-ae49-5f9f6d412758" |
| 141 | + |
| 142 | + |
| 143 | +def test_get_job_workbook_id(server: TSC.Server) -> None: |
| 144 | + response_xml = GET_BY_ID_WORKBOOK.read_text() |
| 145 | + job_id = "bb1aab79-db54-4e96-9dd3-461d8f081d08" |
| 146 | + with requests_mock.mock() as m: |
| 147 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 148 | + job = server.jobs.get_by_id(job_id) |
| 149 | + assert job.workbook_id == "5998aaaf-1abe-4d38-b4d9-bc53e85bdd13" |
| 150 | + |
| 151 | + |
| 152 | +def test_get_job_workbook_name(server: TSC.Server) -> None: |
| 153 | + response_xml = GET_BY_ID_WORKBOOK.read_text() |
| 154 | + job_id = "bb1aab79-db54-4e96-9dd3-461d8f081d08" |
| 155 | + with requests_mock.mock() as m: |
| 156 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 157 | + job = server.jobs.get_by_id(job_id) |
| 158 | + assert job.workbook_name == "Superstore" |
| 159 | + |
| 160 | + |
| 161 | +def test_get_job_datasource_name(server: TSC.Server) -> None: |
| 162 | + response_xml = GET_BY_ID_FAILED_XML.read_text() |
| 163 | + job_id = "777bf7c4-421d-4b2c-a518-11b90187c545" |
| 164 | + with requests_mock.mock() as m: |
| 165 | + m.get(f"{server.jobs.baseurl}/{job_id}", text=response_xml) |
| 166 | + job = server.jobs.get_by_id(job_id) |
| 167 | + assert job.datasource_name == "World Indicators" |
| 168 | + |
| 169 | + |
| 170 | +def test_background_job_str() -> None: |
| 171 | + job = TSC.BackgroundJobItem("ee8c6e70-43b6-11e6-af4f-f7b0d8e20760", datetime.now(), 1, "extractRefresh", "Failed") |
| 172 | + assert not str(job).startswith("<<property") |
| 173 | + assert not repr(job).startswith("<<property") |
| 174 | + assert "BackgroundJobItem" in str(job) |
0 commit comments