def test_publisher_connect_pat(self): """validate publisher issues a pat-authorization header""" # # Verify that a publisher will issue an `Authorization` header when # configure to use a personal access token. token = 'dummy-pat-value' expected_auth_value = 'Bearer {}'.format(token) config = self.config.clone() config.confluence_publish_token = token val = { 'size': 1, 'results': [{ 'name': 'My Space', 'type': 'global', }], } with mock_confluence_instance(config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, val) publisher.init(config) publisher.connect() first_request = daemon.pop_get_request() self.assertIsNotNone(first_request) _, headers = first_request auth_header = headers.get('Authorization') if not auth_header: auth_header = headers.get('authorization') self.assertIsNotNone(auth_header) self.assertEqual(auth_header, expected_auth_value)
def test_publisher_page_base_id_parent_name(self): """validate publisher will search for parent page by name""" # # Verify that a publisher will find a base (pages) identifier, based # off a configured parent page name. expected_page_name = 'refname-31421' config = self.config.clone() config.confluence_parent_page = expected_page_name with mock_confluence_instance(config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(config) publisher.connect() # consume connect request self.assertIsNotNone(daemon.pop_get_request()) # prepare response for a page fetch expected_page_id = '245' page_search_fetch_rsp = { 'size': 1, 'results': [{ 'id': expected_page_id, 'title': 'mock page', 'type': 'page', }], } daemon.register_get_rsp(200, page_search_fetch_rsp) # perform base page id fetch request base_id = publisher.get_base_page_id() # check expected page id returned self.assertEqual(base_id, expected_page_id) # check that the provided page id is set in the request fetch_req = daemon.pop_get_request() self.assertIsNotNone(fetch_req) req_path, _ = fetch_req expected_opt = 'title={}'.format(expected_page_name) self.assertTrue(req_path.startswith('/rest/api/content')) self.assertTrue(expected_opt in expected_opt) # verify that no other request was made daemon.check_unhandled_requests()
def test_publisher_connect_handle_proxy_permission_error(self): """validate publisher reports a proxy-permission error""" # # Verify that the publisher will report a tailored error message when a # configured proxy reports a permission issue. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(407, None) publisher.init(self.config) with self.assertRaises(ConfluenceProxyPermissionError): publisher.connect()
def test_publisher_connect_handle_authentication_error(self): """validate publisher reports an authentication error""" # # Verify that the publisher will report a tailored error message when a # Confluence instance reports an authentication issue. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(401, None) publisher.init(self.config) with self.assertRaises(ConfluenceAuthenticationFailedUrlError): publisher.connect()
def test_publisher_connect_bad_response_code(self): """validate publisher can handle bad response code""" # # Verify that the initial connection event for a publisher can safely # handle a 500 server error on a connection. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(500, None) publisher.init(self.config) with self.assertRaises(ConfluenceBadServerUrlError): publisher.connect()
def test_publisher_connect_invalid_json(self): """validate publisher can handle non-json data""" # # Verify that the initial connection event for a publisher can safely # handle non-JSON data returned from a configured instance (either a # non-Confluence instance or a proxy response). with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, 'Welcome to my website.') publisher.init(self.config) with self.assertRaises(ConfluenceBadServerUrlError): publisher.connect()
def test_publisher_page_store_page_id_dryrun(self): """validate publisher suppress store a page by id with dryrun""" # # Verify that a publisher will handle a id-page update request # properly when the dryrun flag is set. config = self.config.clone() config.confluence_publish_dryrun = True with mock_confluence_instance(config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(config) publisher.connect() # consume connect request self.assertIsNotNone(daemon.pop_get_request()) # prepare response for a page id fetch expected_page_id = 2 page_rsp = { 'id': expected_page_id, 'title': 'mock page', 'type': 'page', } daemon.register_get_rsp(200, page_rsp) page_id = publisher.store_page_by_id('dummy-name', expected_page_id, {}) # check expected page id returned self.assertEqual(page_id, expected_page_id) # check that the provided page id is set in the request fetch_req = daemon.pop_get_request() self.assertIsNotNone(fetch_req) req_path, _ = fetch_req expected_request = '/rest/api/content/{}?'.format(expected_page_id) self.assertTrue(req_path.startswith(expected_request)) # verify that no update request was made daemon.check_unhandled_requests()
def test_publisher_api_bind_path_default(self): """validate publisher includes api bind path""" # # Verify that a publisher will perform requests which target the # `/rest/api` endpoint. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(self.config) publisher.connect() # connect request connect_req = daemon.pop_get_request() self.assertIsNotNone(connect_req) req_path, _ = connect_req self.assertTrue('/rest/api/' in req_path)
def test_publisher_connect_verify_timeout(self): """validate publisher timeout""" # # Verify that a non-served request from a publisher event will timeout. with mock_confluence_instance(self.config, ignore_requests=True), \ autocleanup_publisher(ConfluencePublisher) as publisher: publisher.init(self.config) start = time.time() with self.assertRaises(ConfluenceTimeoutError): publisher.connect() end = time.time() diff = int(end - start) self.assertLessEqual(diff, self.config.confluence_timeout + 1)
def test_publisher_connect_unsupported_json(self): """validate publisher can handle unexpected json data""" # # Verify that the initial connection event for a publisher provides a # bit more strict error checking on the provided JSON data returned. # This is to help deal with a configuration which points to a # non-Confluence instance, but the server provides JSON data in its # response. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: publisher.init(self.config) # random json data rsp = { 'some-data': 'hello', } daemon.register_get_rsp(200, rsp) with self.assertRaises(ConfluenceBadServerUrlError): publisher.connect() # data with a size field, but unrelated data rsp = { 'size': 3, 'data': [ 'item-1', 'item-2', 'item-3', ], } daemon.register_get_rsp(200, rsp) with self.assertRaises(ConfluenceBadServerUrlError): publisher.connect() # result data not matching expected rsp = { 'size': 1, 'results': [{ 'misc-option': 123, }], } daemon.register_get_rsp(200, rsp) with self.assertRaises(ConfluenceBadServerUrlError): publisher.connect()
def test_publisher_api_bind_path_disabled(self): """validate publisher can disable api bind path""" # # Verify that a publisher can perform requests disables the use of # the `/rest/api` endpoint. config = self.config.clone() config.confluence_publish_disable_api_prefix = True with mock_confluence_instance(config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(config) publisher.connect() # connect request connect_req = daemon.pop_get_request() self.assertIsNotNone(connect_req) req_path, _ = connect_req self.assertTrue('/rest/api/' not in req_path)
def test_publisher_page_base_id_parent_none(self): """validate publisher will search for parent page by name""" # # Verify that a publisher will find a base (pages) identifier, based # off a configured parent page name. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(self.config) publisher.connect() # consume connect request self.assertIsNotNone(daemon.pop_get_request()) # prepare response for a page fetch expected_page_id = '416' page_fetch_rsp = { 'size': 1, 'results': [{ 'id': expected_page_id, 'title': 'mock page', 'type': 'page', }], } daemon.register_get_rsp(200, page_fetch_rsp) # request base id which should return nothing, with no request base_id = publisher.get_base_page_id() self.assertIsNone(base_id) # verify that no other request was made daemon.check_unhandled_requests()
def test_publisher_connect_valid_space(self): """validate publisher can find a valid space""" # # Verify that a publisher can query a Confluence instance and cache the # display name for a space. space_name = 'My Space' val = { 'size': 1, 'results': [{ 'name': space_name, 'type': 'global', }], } with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, val) publisher.init(self.config) publisher.connect() self.assertEqual(publisher.space_display_name, space_name)
def test_publisher_page_store_page_id_default(self): """validate publisher will store a page by id (default)""" # # Verify that a publisher can update an existing page by an # identifier value. By default, the update request will ensure # the user configures to not watch the page. with mock_confluence_instance(self.config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(self.config) publisher.connect() # consume connect request self.assertIsNotNone(daemon.pop_get_request()) # prepare response for a page id fetch expected_page_id = 4568 mocked_version = 45 page_fetch_rsp = { 'id': str(expected_page_id), 'title': 'mock page', 'type': 'page', 'version': { 'number': str(mocked_version), }, } daemon.register_get_rsp(200, page_fetch_rsp) # prepare response for update event daemon.register_put_rsp(200, dict(page_fetch_rsp)) # prepare response for unwatch event daemon.register_delete_rsp(200) # perform page update request data = { 'content': 'dummy page data', 'labels': [], } page_id = publisher.store_page_by_id('dummy-name', expected_page_id, data) # check expected page id returned self.assertEqual(page_id, expected_page_id) # check that the provided page id is set in the request fetch_req = daemon.pop_get_request() self.assertIsNotNone(fetch_req) req_path, _ = fetch_req expected_request = '/rest/api/content/{}?'.format(expected_page_id) self.assertTrue(req_path.startswith(expected_request)) # check that an update request is processed update_req = daemon.pop_put_request() self.assertIsNotNone(update_req) # check that the page is unwatched unwatch_req = daemon.pop_delete_request() self.assertIsNotNone(unwatch_req) req_path, _ = unwatch_req ereq = '/rest/api/user/watch/content/{}'.format(expected_page_id) self.assertEqual(req_path, ereq) # verify that no other request was made daemon.check_unhandled_requests()
def test_publisher_page_store_page_id_allow_watch(self): """validate publisher will store a page by id (watch)""" # # Verify that a publisher can update an existing page by an # identifier value. Instance will be configured to watch content, # so any page updates should not trigger an watch event. config = self.config.clone() config.confluence_watch = True with mock_confluence_instance(config) as daemon, \ autocleanup_publisher(ConfluencePublisher) as publisher: daemon.register_get_rsp(200, self.std_space_connect_rsp) publisher.init(config) publisher.connect() # consume connect request self.assertIsNotNone(daemon.pop_get_request()) # prepare response for a page id fetch expected_page_id = 7456 mocked_version = 28 page_fetch_rsp = { 'id': str(expected_page_id), 'title': 'mock page', 'type': 'page', 'version': { 'number': str(mocked_version), }, } daemon.register_get_rsp(200, page_fetch_rsp) # prepare response for update event daemon.register_put_rsp(200, dict(page_fetch_rsp)) # perform page update request data = { 'content': 'dummy page data', 'labels': [], } page_id = publisher.store_page_by_id('dummy-name', expected_page_id, data) # check expected page id returned self.assertEqual(page_id, expected_page_id) # check that the provided page id is set in the request fetch_req = daemon.pop_get_request() self.assertIsNotNone(fetch_req) req_path, _ = fetch_req expected_request = '/rest/api/content/{}?'.format(expected_page_id) self.assertTrue(req_path.startswith(expected_request)) # check that an update request is processed update_req = daemon.pop_put_request() self.assertIsNotNone(update_req) # verify that no other request was made daemon.check_unhandled_requests()
def test_publisher_connect_proxy(self): """validate publisher can find a valid space""" # # Verify that a publisher can query a Confluence instance and cache the # display name for a space. std_space_name = 'My Default Space' std_rsp = { 'size': 1, 'results': [{ 'name': std_space_name, 'type': 'global', }], } proxy_space_name = 'My Proxy Space' proxy_rsp = { 'size': 1, 'results': [{ 'name': proxy_space_name, 'type': 'global', }], } try: with mock_confluence_instance(self.config) as default_daemon,\ mock_confluence_instance() as proxy_daemon: proxy_host, proxy_port = proxy_daemon.server_address proxy_url = 'http://{}:{}/'.format(proxy_host, proxy_port) # check default space is accessible default_daemon.register_get_rsp(200, std_rsp) proxy_daemon.register_get_rsp(200, proxy_rsp) with autocleanup_publisher(ConfluencePublisher) as publisher: publisher.init(self.config) publisher.connect() self.assertEqual(publisher.space_display_name, std_space_name) # check confluence proxy option will go through proxy instance config = self.config.clone() config.confluence_proxy = proxy_url default_daemon.register_get_rsp(200, std_rsp) proxy_daemon.register_get_rsp(200, proxy_rsp) with autocleanup_publisher(ConfluencePublisher) as publisher: publisher.init(config) publisher.connect() self.assertEqual(publisher.space_display_name, proxy_space_name) # check system proxy option will go through proxy instance os.environ['http_proxy'] = proxy_url default_daemon.register_get_rsp(200, std_rsp) proxy_daemon.register_get_rsp(200, proxy_rsp) with autocleanup_publisher(ConfluencePublisher) as publisher: publisher.init(self.config) publisher.connect() self.assertEqual(publisher.space_display_name, proxy_space_name) finally: if 'http_proxy' in os.environ: del os.environ['http_proxy']