import json import os from pathlib import Path from unittest import TestCase from unittest.mock import Mock, call, patch from click.testing import CliRunner from requests import HTTPError from transip_client.cli import DEFAULT_API_URL, DEFAULT_SERVICE, run class RunTestCase(TestCase): def setUp(self): patcher = patch("transip_client.main.requests.get") self.mocked_get = patcher.start() patcher = patch("transip_client.main.requests.put") self.mocked_put = patcher.start() patcher = patch("transip_client.main.requests.Session.send") self.mocked_session = patcher.start() self.runner = CliRunner() def test_simple(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, "foobar.com", env={"TOKEN": "token"}, catch_exceptions=False ) self.assertEqual( logger.output, ["INFO:transip_client.main:Updated domain foobar.com"] ) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) expected_json = json.dumps( { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ] } ) self.mocked_put.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ) def test_error_response(self): self.mocked_get.side_effect = [Mock(text="111.420"), HTTPError] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, ["foobar.com"], env={"TOKEN": "token"}, catch_exceptions=False ) error_log = logger.output[0] self.assertIn("Failed retrieving information for foobar.com", error_log) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) self.mocked_put.assert_not_called() def test_matching_ip(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] result = self.runner.invoke(run, ["foobar.com"], env={"TOKEN": "token"}) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) self.mocked_put.assert_not_called() def test_readonly(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] result = self.runner.invoke( run, ["foobar.com", "--read-only"], env={"TOKEN": "token"} ) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) self.mocked_put.assert_not_called() def test_different_api_url(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, ["foobar.com", "--api-url", "https://other-provider.com"], env={"TOKEN": "token"}, ) self.assertEqual( logger.output, ["INFO:transip_client.main:Updated domain foobar.com"] ) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( "https://other-provider.com/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) expected_json = json.dumps( { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ] } ) self.mocked_put.assert_called_with( "https://other-provider.com/domains/foobar.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ) def test_env_var(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, ["foobar.com"], env={ "TOKEN": "token", "API_URL": "https://new-api.com", }, ) self.assertEqual( logger.output, ["INFO:transip_client.main:Updated domain foobar.com"] ) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( "https://new-api.com/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ) expected_json = json.dumps( { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ] } ) self.mocked_put.assert_called_with( "https://new-api.com/domains/foobar.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ) def test_multi_arg_env_var(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], }, ), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foofoo.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foofoo.com", }, ], } ), ] with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, [], env={"TOKEN": "token", "DOMAINS": "foobar.com foofoo.com"} ) self.assertIsNone(result.exception) self.assertEqual( logger.output, [ "INFO:transip_client.main:Updated domain foobar.com", "INFO:transip_client.main:Updated domain foofoo.com", ], ) self.assertEqual(result.exit_code, 0) expected_calls = [ call(DEFAULT_SERVICE, timeout=10), call( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer token"}, ), call( f"{DEFAULT_API_URL}/domains/foofoo.com/dns", headers={"Authorization": "Bearer token"}, ), ] # use any_order because of the asynchronous requests self.mocked_get.assert_has_calls(expected_calls, any_order=True) expected_json = json.dumps( { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ] } ) expected_calls = [ call( f"{DEFAULT_API_URL}/domains/foobar.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ), call( f"{DEFAULT_API_URL}/domains/foofoo.com/dns", data=expected_json, headers={"Authorization": "Bearer token"}, ), ] self.mocked_put.assert_has_calls(expected_calls, any_order=True) def test_no_domains(self): result = self.runner.invoke(run, [], env={"TOKEN": "token"}) self.assertEqual(result.exit_code, 1) self.assertEqual(str(result.exception), "No domain(s) specified") self.mocked_get.assert_not_called() self.mocked_put.assert_not_called() def test_no_token_or_login_with_private_key_path(self): result = self.runner.invoke(run, ["foobar.com"]) self.assertEqual(result.exit_code, 1) self.assertEqual( str(result.exception), "Either a token or a login name with a path to a private key need" " to be specified", ) self.mocked_get.assert_not_called() self.mocked_put.assert_not_called() def test_login_without_private_key_path(self): result = self.runner.invoke(run, ["foobar.com"], env={"LOGIN": "foo"}) self.assertEqual(result.exit_code, 1) self.assertEqual( str(result.exception), "Both a login name and the path to a private key need to be specified", ) self.mocked_get.assert_not_called() self.mocked_put.assert_not_called() def test_login_with_private_key_path(self): self.mocked_get.side_effect = [ Mock(text="111.420"), Mock( json=lambda: { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.421", } ], "_links": [ { "rel": "self", "link": "https://api.transip.nl/v6/domains/foobar.com/dns", }, { "rel": "domain", "link": "https://api.transip.nl/v6/domains/foobar.com", }, ], } ), ] self.mocked_session.return_value.json.return_value = {"token": "FOOBAR"} private_key_path = ( Path(os.path.dirname(__file__)) / "files" / "test-private-key.pem" ) with self.assertLogs("transip_client.main", level="INFO") as logger: result = self.runner.invoke( run, ["foobar.com"], env={"LOGIN": "foo", "PRIVATE_KEY_PATH": str(private_key_path)}, ) self.assertEqual( logger.output, ["INFO:transip_client.main:Updated domain foobar.com"] ) self.assertEqual(result.exit_code, 0) self.mocked_get.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", headers={"Authorization": "Bearer FOOBAR"}, ) expected_json = json.dumps( { "dnsEntries": [ { "name": "@", "expire": 60, "type": "A", "content": "111.420", } ] } ) self.mocked_put.assert_called_with( f"{DEFAULT_API_URL}/domains/foobar.com/dns", data=expected_json, headers={"Authorization": "Bearer FOOBAR"}, )