OLD | NEW |
(Empty) | |
| 1 # Copyright (c) 2010 Robert Mela |
| 2 # |
| 3 # Permission is hereby granted, free of charge, to any person obtaining a |
| 4 # copy of this software and associated documentation files (the |
| 5 # "Software"), to deal in the Software without restriction, including |
| 6 # without limitation the rights to use, copy, modify, merge, publish, dis- |
| 7 # tribute, sublicense, and/or sell copies of the Software, and to permit |
| 8 # persons to whom the Software is furnished to do so, subject to the fol- |
| 9 # lowing conditions: |
| 10 # |
| 11 # The above copyright notice and this permission notice shall be included |
| 12 # in all copies or substantial portions of the Software. |
| 13 # |
| 14 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| 15 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABIL- |
| 16 # ITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT |
| 17 # SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| 18 # WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 19 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
| 20 # IN THE SOFTWARE. |
| 21 # |
| 22 from tests.compat import mock, unittest |
| 23 |
| 24 import datetime |
| 25 import hashlib |
| 26 import hmac |
| 27 import locale |
| 28 import time |
| 29 |
| 30 import boto.utils |
| 31 from boto.utils import Password |
| 32 from boto.utils import pythonize_name |
| 33 from boto.utils import _build_instance_metadata_url |
| 34 from boto.utils import get_instance_userdata |
| 35 from boto.utils import retry_url |
| 36 from boto.utils import LazyLoadMetadata |
| 37 |
| 38 from boto.compat import json, _thread |
| 39 |
| 40 |
| 41 @unittest.skip("http://bugs.python.org/issue7980") |
| 42 class TestThreadImport(unittest.TestCase): |
| 43 def test_strptime(self): |
| 44 def f(): |
| 45 for m in range(1, 13): |
| 46 for d in range(1,29): |
| 47 boto.utils.parse_ts('2013-01-01T00:00:00Z') |
| 48 |
| 49 for _ in range(10): |
| 50 _thread.start_new_thread(f, ()) |
| 51 |
| 52 time.sleep(3) |
| 53 |
| 54 |
| 55 class TestPassword(unittest.TestCase): |
| 56 """Test basic password functionality""" |
| 57 |
| 58 def clstest(self, cls): |
| 59 """Insure that password.__eq__ hashes test value before compare.""" |
| 60 password = cls('foo') |
| 61 self.assertNotEquals(password, 'foo') |
| 62 |
| 63 password.set('foo') |
| 64 hashed = str(password) |
| 65 self.assertEquals(password, 'foo') |
| 66 self.assertEquals(password.str, hashed) |
| 67 |
| 68 password = cls(hashed) |
| 69 self.assertNotEquals(password.str, 'foo') |
| 70 self.assertEquals(password, 'foo') |
| 71 self.assertEquals(password.str, hashed) |
| 72 |
| 73 def test_aaa_version_1_9_default_behavior(self): |
| 74 self.clstest(Password) |
| 75 |
| 76 def test_custom_hashclass(self): |
| 77 class SHA224Password(Password): |
| 78 hashfunc = hashlib.sha224 |
| 79 |
| 80 password = SHA224Password() |
| 81 password.set('foo') |
| 82 self.assertEquals(hashlib.sha224(b'foo').hexdigest(), str(password)) |
| 83 |
| 84 def test_hmac(self): |
| 85 def hmac_hashfunc(cls, msg): |
| 86 if not isinstance(msg, bytes): |
| 87 msg = msg.encode('utf-8') |
| 88 return hmac.new(b'mysecretkey', msg) |
| 89 |
| 90 class HMACPassword(Password): |
| 91 hashfunc = hmac_hashfunc |
| 92 |
| 93 self.clstest(HMACPassword) |
| 94 password = HMACPassword() |
| 95 password.set('foo') |
| 96 |
| 97 self.assertEquals(str(password), |
| 98 hmac.new(b'mysecretkey', b'foo').hexdigest()) |
| 99 |
| 100 def test_constructor(self): |
| 101 hmac_hashfunc = lambda msg: hmac.new(b'mysecretkey', msg) |
| 102 |
| 103 password = Password(hashfunc=hmac_hashfunc) |
| 104 password.set('foo') |
| 105 self.assertEquals(password.str, |
| 106 hmac.new(b'mysecretkey', b'foo').hexdigest()) |
| 107 |
| 108 |
| 109 class TestPythonizeName(unittest.TestCase): |
| 110 def test_empty_string(self): |
| 111 self.assertEqual(pythonize_name(''), '') |
| 112 |
| 113 def test_all_lower_case(self): |
| 114 self.assertEqual(pythonize_name('lowercase'), 'lowercase') |
| 115 |
| 116 def test_all_upper_case(self): |
| 117 self.assertEqual(pythonize_name('UPPERCASE'), 'uppercase') |
| 118 |
| 119 def test_camel_case(self): |
| 120 self.assertEqual(pythonize_name('OriginallyCamelCased'), |
| 121 'originally_camel_cased') |
| 122 |
| 123 def test_already_pythonized(self): |
| 124 self.assertEqual(pythonize_name('already_pythonized'), |
| 125 'already_pythonized') |
| 126 |
| 127 def test_multiple_upper_cased_letters(self): |
| 128 self.assertEqual(pythonize_name('HTTPRequest'), 'http_request') |
| 129 self.assertEqual(pythonize_name('RequestForHTTP'), 'request_for_http') |
| 130 |
| 131 def test_string_with_numbers(self): |
| 132 self.assertEqual(pythonize_name('HTTPStatus200Ok'), 'http_status_200_ok'
) |
| 133 |
| 134 |
| 135 class TestBuildInstanceMetadataURL(unittest.TestCase): |
| 136 def test_normal(self): |
| 137 # This is the all-defaults case. |
| 138 self.assertEqual(_build_instance_metadata_url( |
| 139 'http://169.254.169.254', |
| 140 'latest', |
| 141 'meta-data/' |
| 142 ), |
| 143 'http://169.254.169.254/latest/meta-data/' |
| 144 ) |
| 145 |
| 146 def test_custom_path(self): |
| 147 self.assertEqual(_build_instance_metadata_url( |
| 148 'http://169.254.169.254', |
| 149 'latest', |
| 150 'dynamic/' |
| 151 ), |
| 152 'http://169.254.169.254/latest/dynamic/' |
| 153 ) |
| 154 |
| 155 def test_custom_version(self): |
| 156 self.assertEqual(_build_instance_metadata_url( |
| 157 'http://169.254.169.254', |
| 158 '1.0', |
| 159 'meta-data/' |
| 160 ), |
| 161 'http://169.254.169.254/1.0/meta-data/' |
| 162 ) |
| 163 |
| 164 def test_custom_url(self): |
| 165 self.assertEqual(_build_instance_metadata_url( |
| 166 'http://10.0.1.5', |
| 167 'latest', |
| 168 'meta-data/' |
| 169 ), |
| 170 'http://10.0.1.5/latest/meta-data/' |
| 171 ) |
| 172 |
| 173 def test_all_custom(self): |
| 174 self.assertEqual(_build_instance_metadata_url( |
| 175 'http://10.0.1.5', |
| 176 '2013-03-22', |
| 177 'user-data' |
| 178 ), |
| 179 'http://10.0.1.5/2013-03-22/user-data' |
| 180 ) |
| 181 |
| 182 class TestRetryURL(unittest.TestCase): |
| 183 def setUp(self): |
| 184 self.urlopen_patch = mock.patch('boto.compat.urllib.request.urlopen') |
| 185 self.opener_patch = mock.patch('boto.compat.urllib.request.build_opener'
) |
| 186 self.urlopen = self.urlopen_patch.start() |
| 187 self.opener = self.opener_patch.start() |
| 188 |
| 189 def tearDown(self): |
| 190 self.urlopen_patch.stop() |
| 191 self.opener_patch.stop() |
| 192 |
| 193 def set_normal_response(self, response): |
| 194 fake_response = mock.Mock() |
| 195 fake_response.read.return_value = response |
| 196 self.urlopen.return_value = fake_response |
| 197 |
| 198 def set_no_proxy_allowed_response(self, response): |
| 199 fake_response = mock.Mock() |
| 200 fake_response.read.return_value = response |
| 201 self.opener.return_value.open.return_value = fake_response |
| 202 |
| 203 def test_retry_url_uses_proxy(self): |
| 204 self.set_normal_response('normal response') |
| 205 self.set_no_proxy_allowed_response('no proxy response') |
| 206 |
| 207 response = retry_url('http://10.10.10.10/foo', num_retries=1) |
| 208 self.assertEqual(response, 'no proxy response') |
| 209 |
| 210 def test_retry_url_using_bytes_and_string_response(self): |
| 211 test_value = 'normal response' |
| 212 fake_response = mock.Mock() |
| 213 |
| 214 # test using unicode |
| 215 fake_response.read.return_value = test_value |
| 216 self.opener.return_value.open.return_value = fake_response |
| 217 response = retry_url('http://10.10.10.10/foo', num_retries=1) |
| 218 self.assertEqual(response, test_value) |
| 219 |
| 220 # test using bytes |
| 221 fake_response.read.return_value = test_value.encode('utf-8') |
| 222 self.opener.return_value.open.return_value = fake_response |
| 223 response = retry_url('http://10.10.10.10/foo', num_retries=1) |
| 224 self.assertEqual(response, test_value) |
| 225 |
| 226 class TestLazyLoadMetadata(unittest.TestCase): |
| 227 |
| 228 def setUp(self): |
| 229 self.retry_url_patch = mock.patch('boto.utils.retry_url') |
| 230 boto.utils.retry_url = self.retry_url_patch.start() |
| 231 |
| 232 def tearDown(self): |
| 233 self.retry_url_patch.stop() |
| 234 |
| 235 def set_normal_response(self, data): |
| 236 # here "data" should be a list of return values in some order |
| 237 fake_response = mock.Mock() |
| 238 fake_response.side_effect = data |
| 239 boto.utils.retry_url = fake_response |
| 240 |
| 241 def test_meta_data_with_invalid_json_format_happened_once(self): |
| 242 # here "key_data" will be stored in the "self._leaves" |
| 243 # when the class "LazyLoadMetadata" initialized |
| 244 key_data = "test" |
| 245 invalid_data = '{"invalid_json_format" : true,}' |
| 246 valid_data = '{ "%s" : {"valid_json_format": true}}' % key_data |
| 247 url = "/".join(["http://169.254.169.254", key_data]) |
| 248 num_retries = 2 |
| 249 |
| 250 self.set_normal_response([key_data, invalid_data, valid_data]) |
| 251 response = LazyLoadMetadata(url, num_retries) |
| 252 self.assertEqual(list(response.values())[0], json.loads(valid_data)) |
| 253 |
| 254 def test_meta_data_with_invalid_json_format_happened_twice(self): |
| 255 key_data = "test" |
| 256 invalid_data = '{"invalid_json_format" : true,}' |
| 257 valid_data = '{ "%s" : {"valid_json_format": true}}' % key_data |
| 258 url = "/".join(["http://169.254.169.254", key_data]) |
| 259 num_retries = 2 |
| 260 |
| 261 self.set_normal_response([key_data, invalid_data, invalid_data]) |
| 262 response = LazyLoadMetadata(url, num_retries) |
| 263 with self.assertRaises(ValueError): |
| 264 response.values()[0] |
| 265 |
| 266 def test_user_data(self): |
| 267 self.set_normal_response(['foo']) |
| 268 |
| 269 userdata = get_instance_userdata() |
| 270 |
| 271 self.assertEqual('foo', userdata) |
| 272 |
| 273 boto.utils.retry_url.assert_called_with( |
| 274 'http://169.254.169.254/latest/user-data', |
| 275 retry_on_404=False, |
| 276 num_retries=5, timeout=None) |
| 277 |
| 278 def test_user_data_timeout(self): |
| 279 self.set_normal_response(['foo']) |
| 280 |
| 281 userdata = get_instance_userdata(timeout=1, num_retries=2) |
| 282 |
| 283 self.assertEqual('foo', userdata) |
| 284 |
| 285 boto.utils.retry_url.assert_called_with( |
| 286 'http://169.254.169.254/latest/user-data', |
| 287 retry_on_404=False, |
| 288 num_retries=2, timeout=1) |
| 289 |
| 290 |
| 291 class TestStringToDatetimeParsing(unittest.TestCase): |
| 292 """ Test string to datetime parsing """ |
| 293 def setUp(self): |
| 294 self._saved = locale.setlocale(locale.LC_ALL) |
| 295 try: |
| 296 locale.setlocale(locale.LC_ALL, 'de_DE.UTF-8') |
| 297 except locale.Error: |
| 298 self.skipTest('Unsupported locale setting') |
| 299 |
| 300 def tearDown(self): |
| 301 locale.setlocale(locale.LC_ALL, self._saved) |
| 302 |
| 303 def test_nonus_locale(self): |
| 304 test_string = 'Thu, 15 May 2014 09:06:03 GMT' |
| 305 |
| 306 # Default strptime shoudl fail |
| 307 with self.assertRaises(ValueError): |
| 308 datetime.datetime.strptime(test_string, boto.utils.RFC1123) |
| 309 |
| 310 # Our parser should succeed |
| 311 result = boto.utils.parse_ts(test_string) |
| 312 |
| 313 self.assertEqual(2014, result.year) |
| 314 self.assertEqual(5, result.month) |
| 315 self.assertEqual(15, result.day) |
| 316 self.assertEqual(9, result.hour) |
| 317 self.assertEqual(6, result.minute) |
| 318 |
| 319 |
| 320 if __name__ == '__main__': |
| 321 unittest.main() |
OLD | NEW |